diff --git a/.gitignore b/.gitignore
index d6e3ba0..df77090 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,5 @@
/db/
.vim
.vscode
+*.blz
+Cargo.lock
diff --git a/Cargo.lock b/Cargo.lock
deleted file mode 100644
index d352282..0000000
--- a/Cargo.lock
+++ /dev/null
@@ -1,609 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 3
-
-[[package]]
-name = "ahash"
-version = "0.8.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
-dependencies = [
- "cfg-if",
- "getrandom",
- "once_cell",
- "version_check",
- "zerocopy",
-]
-
-[[package]]
-name = "aho-corasick"
-version = "1.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "base64"
-version = "0.13.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
-
-[[package]]
-name = "bitvec"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
-dependencies = [
- "funty",
- "radium",
- "tap",
- "wyz",
-]
-
-[[package]]
-name = "blaze"
-version = "0.1.0"
-dependencies = [
- "bson",
- "colored",
- "dotenvy",
- "rand",
- "regex",
- "serde",
- "strum",
- "strum_macros",
-]
-
-[[package]]
-name = "bson"
-version = "2.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d43b38e074cc0de2957f10947e376a1d88b9c4dbab340b590800cc1b2e066b2"
-dependencies = [
- "ahash",
- "base64",
- "bitvec",
- "hex",
- "indexmap",
- "js-sys",
- "once_cell",
- "rand",
- "serde",
- "serde_bytes",
- "serde_json",
- "time",
- "uuid",
-]
-
-[[package]]
-name = "bumpalo"
-version = "3.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
-
-[[package]]
-name = "cfg-if"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-
-[[package]]
-name = "colored"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8"
-dependencies = [
- "lazy_static",
- "windows-sys",
-]
-
-[[package]]
-name = "deranged"
-version = "0.3.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
-dependencies = [
- "powerfmt",
-]
-
-[[package]]
-name = "dotenvy"
-version = "0.15.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
-
-[[package]]
-name = "equivalent"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
-
-[[package]]
-name = "funty"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
-
-[[package]]
-name = "getrandom"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
-dependencies = [
- "cfg-if",
- "libc",
- "wasi",
-]
-
-[[package]]
-name = "hashbrown"
-version = "0.14.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
-
-[[package]]
-name = "heck"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
-
-[[package]]
-name = "hex"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
-
-[[package]]
-name = "indexmap"
-version = "2.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
-dependencies = [
- "equivalent",
- "hashbrown",
-]
-
-[[package]]
-name = "itoa"
-version = "1.0.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
-
-[[package]]
-name = "js-sys"
-version = "0.3.69"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
-dependencies = [
- "wasm-bindgen",
-]
-
-[[package]]
-name = "lazy_static"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
-
-[[package]]
-name = "libc"
-version = "0.2.154"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
-
-[[package]]
-name = "log"
-version = "0.4.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
-
-[[package]]
-name = "memchr"
-version = "2.7.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
-
-[[package]]
-name = "num-conv"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
-
-[[package]]
-name = "once_cell"
-version = "1.19.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
-
-[[package]]
-name = "powerfmt"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
-
-[[package]]
-name = "ppv-lite86"
-version = "0.2.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
-
-[[package]]
-name = "proc-macro2"
-version = "1.0.81"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
-dependencies = [
- "unicode-ident",
-]
-
-[[package]]
-name = "quote"
-version = "1.0.36"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
-dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "radium"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
-
-[[package]]
-name = "rand"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
-dependencies = [
- "libc",
- "rand_chacha",
- "rand_core",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
-dependencies = [
- "ppv-lite86",
- "rand_core",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
-dependencies = [
- "getrandom",
-]
-
-[[package]]
-name = "regex"
-version = "1.10.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-automata",
- "regex-syntax",
-]
-
-[[package]]
-name = "regex-automata"
-version = "0.4.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-syntax",
-]
-
-[[package]]
-name = "regex-syntax"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
-
-[[package]]
-name = "rustversion"
-version = "1.0.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47"
-
-[[package]]
-name = "ryu"
-version = "1.0.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
-
-[[package]]
-name = "serde"
-version = "1.0.200"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f"
-dependencies = [
- "serde_derive",
-]
-
-[[package]]
-name = "serde_bytes"
-version = "0.11.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "serde_derive"
-version = "1.0.200"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "serde_json"
-version = "1.0.116"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813"
-dependencies = [
- "indexmap",
- "itoa",
- "ryu",
- "serde",
-]
-
-[[package]]
-name = "strum"
-version = "0.26.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29"
-
-[[package]]
-name = "strum_macros"
-version = "0.26.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946"
-dependencies = [
- "heck",
- "proc-macro2",
- "quote",
- "rustversion",
- "syn",
-]
-
-[[package]]
-name = "syn"
-version = "2.0.60"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "tap"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
-
-[[package]]
-name = "time"
-version = "0.3.36"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
-dependencies = [
- "deranged",
- "itoa",
- "num-conv",
- "powerfmt",
- "serde",
- "time-core",
- "time-macros",
-]
-
-[[package]]
-name = "time-core"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
-
-[[package]]
-name = "time-macros"
-version = "0.2.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
-dependencies = [
- "num-conv",
- "time-core",
-]
-
-[[package]]
-name = "unicode-ident"
-version = "1.0.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
-
-[[package]]
-name = "uuid"
-version = "1.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
-dependencies = [
- "getrandom",
- "serde",
-]
-
-[[package]]
-name = "version_check"
-version = "0.9.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
-
-[[package]]
-name = "wasi"
-version = "0.11.0+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
-
-[[package]]
-name = "wasm-bindgen"
-version = "0.2.92"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
-dependencies = [
- "cfg-if",
- "wasm-bindgen-macro",
-]
-
-[[package]]
-name = "wasm-bindgen-backend"
-version = "0.2.92"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
-dependencies = [
- "bumpalo",
- "log",
- "once_cell",
- "proc-macro2",
- "quote",
- "syn",
- "wasm-bindgen-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-macro"
-version = "0.2.92"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
-dependencies = [
- "quote",
- "wasm-bindgen-macro-support",
-]
-
-[[package]]
-name = "wasm-bindgen-macro-support"
-version = "0.2.92"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
- "wasm-bindgen-backend",
- "wasm-bindgen-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-shared"
-version = "0.2.92"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
-
-[[package]]
-name = "windows-sys"
-version = "0.48.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
-dependencies = [
- "windows-targets",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
-dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
-]
-
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
-
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
-
-[[package]]
-name = "wyz"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
-dependencies = [
- "tap",
-]
-
-[[package]]
-name = "zerocopy"
-version = "0.7.32"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
-dependencies = [
- "zerocopy-derive",
-]
-
-[[package]]
-name = "zerocopy-derive"
-version = "0.7.32"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
diff --git a/Cargo.toml b/Cargo.toml
index 2dafd0b..f6db798 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,9 +6,16 @@ edition = "2021"
[dependencies]
regex = "1.10"
bson = "2.10"
+uuid = "1.8.0"
+ctrlc = "3.4.4"
rand = "0.8"
strum = "0.26"
+chrono = "0.4.38"
colored = "2.1.0"
strum_macros = "0.26"
dotenvy = "0.15"
-serde = { version = "1.0", features = ["derive"] }
\ No newline at end of file
+crossbeam = "0.8.4"
+http-body-util = "0.1"
+hyper = { version = "1", features = ["full"] }
+tokio = { version = "1", features = ["full"] }
+serde = { version = "1", features = ["derive"] }
diff --git a/README.md b/README.md
index fc02ba4..20bc612 100644
--- a/README.md
+++ b/README.md
@@ -5,87 +5,103 @@ And of course it's blazingly fast
## ✨ Blaze Language Syntax (currently uncompleted)
-1. Manager.blz (used to raise the database with packages, settings and ispects included)
+1. Manage file (configuration and attaching to a datafile are only available in this type of files)
```ruby
manage (
- packages = "./packages",
- max_connections = 8,
- port = "6980",
- host = "127.0.0.1"
+ address = "127.0.0.1:3306",
+ sessions_limit = 250,
+ session_lifetime = 120
);
-import users:all, animals:species;
-
-inspect all;
-
-attach "./data";
+import scheme[target];
+attach()"./data.dblz";
```
-2. Basic Scheme and some declarations
+2. A package with functions, events, enums, plans, lanes, and tables
```ruby
package scheme;
-enum Gender: str {
- Male,
- Female,
- Other,
- Unspecified
+// Enum Members have their own IDs
+// so you can access them without any changes but here.
+enum Gender {
+ Unspecified = [ 0, "unknown" ],
+ Male = [ 1, "boy" ],
+ Female = [ 2, "girl" ],
+ Other = [ 3, "other" ],
};
-enum TargetAudience: str {
- Kids,
- Everyone,
- Adults,
- Elderly
+// Snake_case is the standard
+mut variable_showcase = Gender.Male; // "boy"
+fin best_gender: str = Gender[0]; // "unknown"
+
+enum TargetAudience {
+ Kids = [ 0, lower(self [0]) ],
+ Everyone = [ 1, lower(self [1]) ],
+ Adults = [ 2, lower(self [2]) ],
+ Elderly = [ 3, lower(self [3]) ],
};
+// Plan is a data structure that can be used for instances creation,
+// and tables & lanes implementation
+plan Geo {
+ x: float, y: float
+}
+
+// Table ID's are created without explicit declaration,
+// Exclamation mark means the field value must be unique
table countries {
- name: str <=50,
- alpha2: str 2,
- alpha3: str 3,
- geolocation: geo;
+ name: !str[ <50 ],
+ alpha2: !str[ =2 ],
+ alpha3: !str[ =2 ],
+ geolocation: Geo;
};
+// db.countries is some sort of a foreign key,
+// Question Mark means the country field can be null
table accounts: uuid {
- name: str <=30 = format("User{}", self.id),
- bio: str <=200,
- password: str,
- gender: Gender = Gender.Unspecified,
- age: int >0 <100,
- country: &countries?,
- created_at: datetime = "now";
+ name: str[ <=30 ] = format("User{}", id),
+ bio: str[ <=200 ],
+ password: str[ >8 ],
+ age: int[ >0 <100 ],
+ gender: Gender = Gender.Unspecified,
+ created_at: datetime = "now";
+ country: db.countries?,
};
-
-table products: uuid {
- title: str = "product",
- seller: &accounts,
- price: float >= 0,
+plan Products {
+ title: str = "product",
+ created_at: datetime = "now",
+ seller: db.accounts,
+ price: float[>=0],
description: str,
- created_at: datetime = "now",
- sales_count: int >=0 ,
- audience: TargetAudience = TargetAudience.Everyone;
+ sales_count: int[>-1],
+ audience: TargetAudience = TargetAudience.Everyone;
}
+table products(Products): uuid;
table shopping_cart {
- account: &account!,
- added_products: &products[],
- last_update: datetime = "now";
+ account: !db.accounts,
+ added_products: !arr[db.products],
+ last_update: timestamp[local] = "now";
};
-event shopping_cart_update (&shopping_cart.added_products, "change") {
- &shopping_cart.last_update = "now";
+// Should be considered
+event shopping_cart_updated("update", db.shopping_cart): x {
+ x.last_update = "now";
};
function get_population_of(country_name: str): int {
- fin country_id = &countries.{self.name=country_name}.id;
- count(&accounts.{self.country=country_id})
+ // db.contries.() is a call to a database,
+ // the condition inside parenthesis filters the "rows";
+ // .id returns an array of all the IDs of the filtered rows
+ fin country_id = db.countries.(self.name==country_name).id;
+ count(db.accounts.(country=country_id))
};
-function get_total_cart_price(products: &products[]): float {
+function get_total_cart_price(products: Products): float {
mut total: float = 0;
product of products {
total += product.price;
};
total
-}
\ No newline at end of file
+}
diff --git a/src/db/create_db.rs b/src/db/create_db.rs
deleted file mode 100644
index 21931ad..0000000
--- a/src/db/create_db.rs
+++ /dev/null
@@ -1,56 +0,0 @@
-use std::fs::{self, File};
-use std::io::{Result, Write};
-use std::path::{Path, PathBuf};
-
-pub const OFFICIAL_REPOSITORY: &str = "https://github.com/Reeliks/blaze";
-
-pub fn create_db_structure(path_to_db: &str) -> Result<()> {
- let mut db_workdir_path_buf = PathBuf::from(&path_to_db);
- db_workdir_path_buf.push("datablaze");
- create_db_folders(&db_workdir_path_buf)?;
-
- let is_manage_file_created: bool = create_manage_file(&db_workdir_path_buf)?;
- if is_manage_file_created {
- println!("manage.blz has been created")
- } else {
- println!("manage.blz already exists; skipping...");
- }
- println!("\nA new datablaze has been structured. Use 'blaze --help' to see the commands.\nTo contribute the development process, check out the official repository:\n{}", OFFICIAL_REPOSITORY);
-
- Ok(())
-}
-
-fn create_db_folders(db_path_buf: &Path) -> Result<()> {
- for folder in ["data", "model"] {
- let mut cloned_db_path_buf = db_path_buf.to_path_buf();
- cloned_db_path_buf.push(folder);
- fs::create_dir_all(cloned_db_path_buf)?;
- }
- Ok(())
-}
-
-fn create_manage_file(path_to_db_buf: &Path) -> Result {
- let mut managing_file_path_buf = path_to_db_buf.to_path_buf();
- let manage_file_content = br#"manage (
- tmax_connections = 3,
- work_dir = "/",
- backups_dir = "backups/"
-);
-
-attach "/data/main;"#;
-
- managing_file_path_buf.push("manage.blz");
- if let Err(e) = fs::metadata(managing_file_path_buf.to_str().unwrap()) {
- match e.kind() {
- std::io::ErrorKind::NotFound => {
- let mut managing_file = File::create(&mut managing_file_path_buf)?;
- managing_file.write_all(manage_file_content)?;
- return Ok(true);
- }
- _ => {
- panic!("{}", e);
- }
- };
- };
- Ok(false)
-}
diff --git a/src/fs/filesystem.rs b/src/db/filesystem.rs
similarity index 93%
rename from src/fs/filesystem.rs
rename to src/db/filesystem.rs
index 97e9e44..2156997 100644
--- a/src/fs/filesystem.rs
+++ b/src/db/filesystem.rs
@@ -53,7 +53,10 @@ impl Fs {
if *key == table_name {
value.push(data.clone());
} else {
- doc.insert(table_name.clone(), Bson::Array(vec![Bson::from(data.clone())]));
+ doc.insert(
+ table_name.clone(),
+ Bson::Array(vec![Bson::from(data.clone())]),
+ );
}
}
diff --git a/src/db/mod.rs b/src/db/mod.rs
index 2db1a55..a286318 100644
--- a/src/db/mod.rs
+++ b/src/db/mod.rs
@@ -1 +1 @@
-pub mod create_db;
+pub mod filesystem;
diff --git a/src/fs/mod.rs b/src/fs/mod.rs
deleted file mode 100644
index a286318..0000000
--- a/src/fs/mod.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub mod filesystem;
diff --git a/src/lib.rs b/src/lib.rs
index 05050dc..48ecf0b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,6 @@
pub mod db;
-pub mod fs;
+pub mod prelude;
+pub mod routine;
pub mod scripting;
pub mod server;
pub mod shell;
diff --git a/src/main.rs b/src/main.rs
index 5427807..17f997b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,8 +1,6 @@
-use std::io::Result;
+use blaze::shell::command_handler::ShellCommandHandler;
-use blaze::shell;
-
-fn main() -> Result<()> {
- shell::handling::handle_command_arguments()?;
- Ok(())
+#[tokio::main]
+async fn main() {
+ ShellCommandHandler::new().handle_command().await.unwrap();
}
diff --git a/src/prelude.rs b/src/prelude.rs
new file mode 100644
index 0000000..0f1c9b2
--- /dev/null
+++ b/src/prelude.rs
@@ -0,0 +1 @@
+pub use std::io::ErrorKind;
diff --git a/src/routine/formatting.rs b/src/routine/formatting.rs
new file mode 100644
index 0000000..62e6e97
--- /dev/null
+++ b/src/routine/formatting.rs
@@ -0,0 +1,11 @@
+use colored::Colorize;
+
+pub trait MessagesFormatting {
+ fn into_hint(self) -> String;
+}
+
+impl MessagesFormatting for String {
+ fn into_hint(self) -> String {
+ format!(" * {}", self.bright_blue())
+ }
+}
diff --git a/src/routine/info_channel.rs b/src/routine/info_channel.rs
new file mode 100644
index 0000000..18f6258
--- /dev/null
+++ b/src/routine/info_channel.rs
@@ -0,0 +1,39 @@
+use crossbeam::channel::{bounded, Sender};
+use std::thread;
+
+use std::io::Result;
+
+pub struct InfoChannel {
+ sender: Option>,
+}
+
+impl InfoChannel {
+ pub fn new(sender: Option>) -> Self {
+ InfoChannel { sender }
+ }
+
+ pub fn send(self, message: String) -> Result<()> {
+ if let Some(channel) = self.sender {
+ channel.send(message).unwrap();
+ }
+ Ok(())
+ }
+}
+
+impl Clone for InfoChannel {
+ fn clone(&self) -> Self {
+ InfoChannel {
+ sender: self.sender.clone(),
+ }
+ }
+}
+
+pub fn get_console_info_channel() -> InfoChannel {
+ let (tx, rx) = bounded(100);
+ thread::spawn(move || loop {
+ if let Ok(received) = rx.try_recv() {
+ println!("{}", received);
+ }
+ });
+ InfoChannel::new(Some(tx))
+}
diff --git a/src/routine/mod.rs b/src/routine/mod.rs
new file mode 100644
index 0000000..cedbbb9
--- /dev/null
+++ b/src/routine/mod.rs
@@ -0,0 +1,2 @@
+pub mod formatting;
+pub mod info_channel;
diff --git a/src/scripting/ast/binary_operator.rs b/src/scripting/ast/binary_operator.rs
index a65a2ab..dce655b 100644
--- a/src/scripting/ast/binary_operator.rs
+++ b/src/scripting/ast/binary_operator.rs
@@ -1,6 +1,4 @@
-use crate::scripting::tokens::TokenType;
-
-use super::expression::ExpressionNode;
+use super::{expression::ExpressionNode, tokens::TokenType};
pub struct BinaryOperatorNode {
_operator: TokenType,
diff --git a/src/scripting/ast/boolean.rs b/src/scripting/ast/boolean.rs
index 696a1d4..c9a2733 100644
--- a/src/scripting/ast/boolean.rs
+++ b/src/scripting/ast/boolean.rs
@@ -1,8 +1,6 @@
use std::io;
-use crate::scripting::tokens::TokenType;
-
-use super::expression::ExpressionNode;
+use super::{expression::ExpressionNode, tokens::TokenType};
pub struct BooleanNode {
_state: bool,
diff --git a/src/scripting/ast/functional_return.rs b/src/scripting/ast/functional_return.rs
index 4d6ea13..91e9ec6 100644
--- a/src/scripting/ast/functional_return.rs
+++ b/src/scripting/ast/functional_return.rs
@@ -1,7 +1,7 @@
use super::expression::ExpressionNode;
pub struct FunctionalReturnNode {
- _value: Option>
+ _value: Option>,
}
impl ExpressionNode for FunctionalReturnNode {
@@ -12,8 +12,6 @@ impl ExpressionNode for FunctionalReturnNode {
impl FunctionalReturnNode {
pub fn new(value: Option>) -> Self {
- FunctionalReturnNode {
- _value: value
- }
+ FunctionalReturnNode { _value: value }
}
}
diff --git a/src/scripting/ast/loop_control.rs b/src/scripting/ast/loop_control.rs
index 08c9500..3882a22 100644
--- a/src/scripting/ast/loop_control.rs
+++ b/src/scripting/ast/loop_control.rs
@@ -2,11 +2,11 @@ use super::expression::ExpressionNode;
pub enum LoopControlType {
Continue,
- Break
+ Break,
}
pub struct LoopControlNode {
- _control_type: LoopControlType
+ _control_type: LoopControlType,
}
impl ExpressionNode for LoopControlNode {
@@ -18,7 +18,7 @@ impl ExpressionNode for LoopControlNode {
impl LoopControlNode {
pub fn new(control_type: LoopControlType) -> Self {
LoopControlNode {
- _control_type: control_type
+ _control_type: control_type,
}
}
}
diff --git a/src/scripting/ast/mod.rs b/src/scripting/ast/mod.rs
index 87478f5..d2edd18 100644
--- a/src/scripting/ast/mod.rs
+++ b/src/scripting/ast/mod.rs
@@ -1,3 +1,5 @@
+pub mod tokens;
+
pub mod binary_operator;
pub mod body;
pub mod boolean;
@@ -5,14 +7,14 @@ pub mod call;
pub mod conditional_tree;
pub mod expression;
pub mod function_declaration;
-pub mod identifier;
-pub mod member;
pub mod functional_return;
-pub mod while_loop;
+pub mod identifier;
pub mod loop_control;
+pub mod member;
pub mod null;
pub mod number;
pub mod parameter;
pub mod string;
pub mod unary_operator;
pub mod variable_declaration;
+pub mod while_loop;
diff --git a/src/scripting/tokens.rs b/src/scripting/ast/tokens.rs
similarity index 96%
rename from src/scripting/tokens.rs
rename to src/scripting/ast/tokens.rs
index 8fa5486..d10d87d 100644
--- a/src/scripting/tokens.rs
+++ b/src/scripting/ast/tokens.rs
@@ -61,6 +61,9 @@ pub enum TokenType {
Fin,
Function,
Enum,
+ Table,
+ Lane,
+ Plan,
// Brackets
LPar,
RPar,
@@ -95,6 +98,9 @@ impl TokenType {
TokenType::Mut => r"mut\b",
TokenType::Fin => r"fin\b",
TokenType::Enum => r"enum\b",
+ TokenType::Lane => r"lane\b",
+ TokenType::Table => r"table\b",
+ TokenType::Plan => r"plan\b",
TokenType::True => r"true\b",
TokenType::False => r"false\b",
TokenType::Null => r"null\b",
diff --git a/src/scripting/ast/unary_operator.rs b/src/scripting/ast/unary_operator.rs
index a67e343..8d99dcf 100644
--- a/src/scripting/ast/unary_operator.rs
+++ b/src/scripting/ast/unary_operator.rs
@@ -1,6 +1,7 @@
-use crate::scripting::tokens::{TokenSide, TokenType};
-
-use super::expression::ExpressionNode;
+use super::{
+ expression::ExpressionNode,
+ tokens::{TokenSide, TokenType},
+};
pub struct UnaryOperatorNode {
_operator: TokenType,
diff --git a/src/scripting/ast/while_loop.rs b/src/scripting/ast/while_loop.rs
index a1f63d8..0065675 100644
--- a/src/scripting/ast/while_loop.rs
+++ b/src/scripting/ast/while_loop.rs
@@ -2,7 +2,7 @@ use super::{body::BodyNode, expression::ExpressionNode};
pub struct WhileLoopNode {
_condition: Box,
- _body: BodyNode
+ _body: BodyNode,
}
impl ExpressionNode for WhileLoopNode {
@@ -15,7 +15,7 @@ impl WhileLoopNode {
pub fn new(condition: Box, body: BodyNode) -> Self {
WhileLoopNode {
_condition: condition,
- _body: body
+ _body: body,
}
}
}
diff --git a/src/scripting/context.rs b/src/scripting/context.rs
index 8360fc3..ee1b6ac 100644
--- a/src/scripting/context.rs
+++ b/src/scripting/context.rs
@@ -5,7 +5,7 @@ pub struct Context {
}
impl Context {
- fn new(code_source: String) -> Self {
+ pub fn new(code_source: String) -> Self {
Context {
code_source,
position: 0,
diff --git a/src/scripting/executor.rs b/src/scripting/executor.rs
index 06e6693..4f3fbef 100644
--- a/src/scripting/executor.rs
+++ b/src/scripting/executor.rs
@@ -1,13 +1,16 @@
-pub struct Executor {}
+use super::{ast::body::BodyNode, context::Context};
+use std::io::Result;
-impl Executor {
- pub fn new() -> Self {
- Executor {}
- }
+pub struct Executor<'a> {
+ _context: &'a mut Context,
}
-impl Default for Executor {
- fn default() -> Self {
- Self::new()
+impl<'a> Executor<'a> {
+ pub fn new(context: &'a mut Context) -> Self {
+ Self { _context: context }
+ }
+
+ pub fn execute(self, _nodes: BodyNode) -> Result<()> {
+ Ok(())
}
}
diff --git a/src/scripting/lexer.rs b/src/scripting/lexer.rs
index 4c9673c..0e96f4b 100644
--- a/src/scripting/lexer.rs
+++ b/src/scripting/lexer.rs
@@ -3,21 +3,26 @@ use regex::Regex;
use std::io;
use strum::IntoEnumIterator;
+use crate::routine::info_channel::InfoChannel;
+
+use super::ast::tokens::{Token, TokenType, WHITESPACE_TOKENS};
use super::context::Context;
-use super::tokens::{Token, TokenType, WHITESPACE_TOKENS};
+use crate::prelude::*;
pub struct Lexer {
pub context: Context,
code: String,
tokens: Vec,
+ info_channel: InfoChannel, // Connect it only on debugging
}
impl Lexer {
- pub fn new(code: String) -> Self {
- Lexer {
+ pub fn new(code: String, info_channel: InfoChannel) -> Self {
+ Self {
code,
context: Context::default(),
tokens: vec![],
+ info_channel,
}
}
@@ -31,20 +36,23 @@ impl Lexer {
Ok(proceed_parsing) => {
if !proceed_parsing {
self.tokens.retain(|token| !token.is_type(TokenType::Space));
- return Ok(self.tokens);
+ return Ok(self.tokens.clone());
}
let last_token = self.tokens.last().unwrap();
if !WHITESPACE_TOKENS.contains(&last_token.token_type) {
let start_position =
1 + self.context.position - last_token.value.len() as u64;
- println!(
- "{}:{} = {}",
- start_position, last_token.value, last_token.token_type
- );
+ self.info_channel
+ .clone()
+ .send(format!(
+ "{}:{} = {}",
+ start_position, last_token.value, last_token.token_type
+ ))
+ .unwrap();
}
}
Err(err) => {
- eprintln!("{}", err);
+ self.info_channel.send(err.to_string()).unwrap();
break;
}
};
@@ -61,7 +69,11 @@ impl Lexer {
let token_regex_string = TokenType::regex_str(&token_type);
let token_regex = Regex::new(&format!(r#"^{}"#, token_regex_string)).unwrap();
if let Some(matches) = token_regex.find(positioned_code) {
- let matched_str = matches.as_str();
+ let matched_str = if token_type == TokenType::Number {
+ matches.as_str().replace('_', "")
+ } else {
+ matches.as_str().to_string()
+ };
self.tokens.push(Token {
token_type,
start: self.context.position,
@@ -78,7 +90,7 @@ impl Lexer {
}
}
Err(io::Error::new(
- io::ErrorKind::Other,
+ ErrorKind::Other,
format!(
"{}: '{}' {} <-= at {}:{}:{}",
"Lexical Error".bright_red(),
@@ -106,7 +118,7 @@ impl Lexer {
let last_token_is_number = last_token.is_type(TokenType::Number);
if last_token_is_number && current_token_is_alphanumeric {
return Err(io::Error::new(
- io::ErrorKind::Other,
+ ErrorKind::Other,
format!(
"{}: numbers cannot end with alphanumeric <-= at {}:{}:{}",
"Lexical Error".bright_red(),
@@ -142,9 +154,9 @@ impl Lexer {
|| left_side_unresolved_chars_regex.is_match(char_before)
{
return Err(io::Error::new(
- io::ErrorKind::Other,
+ ErrorKind::Other,
format!(
- "{}: \"{}\" near a string with no space between <-= at {}:{}:{}",
+ "{}: \"{}\" before a string with no space between <-= at {}:{}:{}",
"Lexical Error".bright_red(),
char_before,
self.context.code_source,
@@ -163,7 +175,7 @@ impl Lexer {
.to_string();
if both_sides_unresolved_chars_regex.is_match(char_after) {
return Err(io::Error::new(
- io::ErrorKind::Other,
+ ErrorKind::Other,
format!(
"{}: \"{}\" after a string with no space between <-= at {}:{}:{}",
"Lexical Error".bright_red(),
diff --git a/src/scripting/mod.rs b/src/scripting/mod.rs
index 604a387..c9fc292 100644
--- a/src/scripting/mod.rs
+++ b/src/scripting/mod.rs
@@ -3,4 +3,3 @@ pub mod context;
pub mod executor;
pub mod lexer;
pub mod parser;
-pub mod tokens;
diff --git a/src/scripting/parser.rs b/src/scripting/parser.rs
index 0de2db8..5334792 100644
--- a/src/scripting/parser.rs
+++ b/src/scripting/parser.rs
@@ -1,26 +1,30 @@
-use super::ast::binary_operator::BinaryOperatorNode;
-use super::ast::body::BodyNode;
-use super::ast::boolean::BooleanNode;
-use super::ast::call::CallNode;
-use super::ast::conditional_tree::{ConditionalTreeNode, Conditions};
-use super::ast::expression::ExpressionNode;
-use super::ast::function_declaration::FunctionDeclarationNode;
-use super::ast::functional_return::FunctionalReturnNode;
-use super::ast::identifier::IdentifierNode;
-use super::ast::loop_control::{LoopControlNode, LoopControlType};
-use super::ast::member::MemberNode;
-use super::ast::null::NullNode;
-use super::ast::number::NumberNode;
-use super::ast::parameter::{Parameter, ParameterType, Parameters};
-use super::ast::string::StringNode;
-use super::ast::unary_operator::UnaryOperatorNode;
-use super::ast::variable_declaration::VariableDeclaration;
-use super::ast::while_loop::WhileLoopNode;
-use super::context::Context;
-use super::tokens::{
- Token, TokenSide, TokenType, BINARY_OPERATOR_TOKENS, FORMULA_TOKENS, UNARY_OPERATOR_TOKENS,
- VARIABLE_ASSIGNMENT_TOKENS,
+use super::ast::{
+ binary_operator::BinaryOperatorNode,
+ body::BodyNode,
+ boolean::BooleanNode,
+ call::CallNode,
+ conditional_tree::{ConditionalTreeNode, Conditions},
+ expression::ExpressionNode,
+ function_declaration::FunctionDeclarationNode,
+ functional_return::FunctionalReturnNode,
+ identifier::IdentifierNode,
+ loop_control::{LoopControlNode, LoopControlType},
+ member::MemberNode,
+ null::NullNode,
+ number::NumberNode,
+ parameter::{Parameter, ParameterType, Parameters},
+ string::StringNode,
+ tokens::{
+ Token, TokenSide, TokenType, BINARY_OPERATOR_TOKENS, FORMULA_TOKENS, UNARY_OPERATOR_TOKENS,
+ VARIABLE_ASSIGNMENT_TOKENS,
+ },
+ unary_operator::UnaryOperatorNode,
+ variable_declaration::VariableDeclaration,
+ while_loop::WhileLoopNode,
};
+use super::context::Context;
+use crate::routine::info_channel::InfoChannel;
+
use colored::*;
use rand::seq::SliceRandom;
use std::io::{self, Result};
@@ -30,16 +34,21 @@ pub struct Parser {
context: Context,
parser_position: u64,
+ _info_channel: InfoChannel,
syntax_error_marking: ColoredString,
}
impl Parser {
- pub fn new(tokens: Vec) -> Self {
+ pub fn new(
+ tokens: Vec,
+ info_channel: InfoChannel, // Connect it only on debugging
+ ) -> Self {
Parser {
context: Context::default(),
tokens,
parser_position: 0,
+ _info_channel: info_channel,
syntax_error_marking: "Syntax Error".bright_red(),
}
}
@@ -274,11 +283,9 @@ impl Parser {
)))
}
TokenType::Return => {
- let returned_formula_node
- = if self.move_if_next_token_is(FORMULA_TOKENS.to_vec()) {
+ let returned_formula_node = if self.move_if_next_token_is(FORMULA_TOKENS.to_vec()) {
Some(self.require_formula()?)
- }
- else {
+ } else {
None
};
Ok(Box::new(FunctionalReturnNode::new(returned_formula_node)))
@@ -289,15 +296,11 @@ impl Parser {
self.move_position();
Ok(Box::new(WhileLoopNode::new(
condition_node,
- self.require_body()?
+ self.require_body()?,
)))
}
- TokenType::Continue => {
- Ok(Box::new(LoopControlNode::new(LoopControlType::Continue)))
- }
- TokenType::Break => {
- Ok(Box::new(LoopControlNode::new(LoopControlType::Break)))
- }
+ TokenType::Continue => Ok(Box::new(LoopControlNode::new(LoopControlType::Continue))),
+ TokenType::Break => Ok(Box::new(LoopControlNode::new(LoopControlType::Break))),
_ => Err(io::Error::new(
io::ErrorKind::Other,
format!(
@@ -428,12 +431,11 @@ impl Parser {
));
}
};
- self.move_position();
- if self.get_current_token().is_ok()
- && self.get_current_token()?.is_type(TokenType::Comma)
- {
- self.move_position();
+ if !self.move_if_next_token_is(vec![TokenType::Comma]) {
+ break;
}
+ self.move_position();
+ continue;
}
Ok(arguments)
}
diff --git a/src/server/client_session.rs b/src/server/client_session.rs
new file mode 100644
index 0000000..196c612
--- /dev/null
+++ b/src/server/client_session.rs
@@ -0,0 +1,40 @@
+use std::time::Duration;
+
+use chrono::{DateTime, Local};
+use uuid::Uuid;
+
+// A client connection works like a session
+pub struct ClientSession {
+ pub id: Uuid,
+ pub _creation_time: DateTime,
+ pub expiration_time: Option>,
+}
+
+impl ClientSession {
+ pub fn new(creation_time: DateTime) -> Self {
+ Self {
+ id: Uuid::new_v4(),
+ _creation_time: creation_time,
+ expiration_time: None,
+ }
+ }
+
+ pub fn is_active(&self) -> bool {
+ if self.expiration_time.is_none() {
+ return true;
+ }
+ Local::now() < self.expiration_time.unwrap()
+ }
+
+ pub fn activate_indefinitely(&mut self) {
+ self.expiration_time = None
+ }
+
+ pub fn desactivate(&mut self) {
+ self.expiration_time = Some(Local::now())
+ }
+
+ pub fn activate_temporairly(&mut self, lifetime: Duration) {
+ self.expiration_time = Some(Local::now() + lifetime)
+ }
+}
diff --git a/src/server/config.rs b/src/server/config.rs
deleted file mode 100644
index 2517488..0000000
--- a/src/server/config.rs
+++ /dev/null
@@ -1,81 +0,0 @@
-use dotenvy::dotenv;
-use regex::Regex;
-use std::{env, ffi::OsStr, path::Path};
-
-pub struct Config {
- pub host: String,
- pub port: String,
- pub manager_file: String,
- pub password: String,
-}
-
-impl Config {
- fn value(user_key: String) -> Option {
- dotenv().expect(".env file not found");
-
- let user_key = Regex::new(r"env\.(\w+)")
- .unwrap()
- .captures(&user_key)?
- .get(1)?
- .as_str();
-
- env::vars()
- .filter(|(key, _)| *key == user_key)
- .map(|(_, value)| value)
- .next()
- }
-
- pub fn blz_exists(path: &String) -> bool {
- let blz_file = Path::new(&path);
-
- if !blz_file.exists() || blz_file.extension().unwrap_or(OsStr::new("")) != "blz" {
- return false;
- }
- true
- }
-
- pub fn parse_arguments(args: Vec) -> Option {
- let default = Self::default();
- let mut host = default.host;
- let mut port = default.port;
- let mut manager_file = default.manager_file;
- let mut password = default.password;
-
- for arg in 0..args.len() {
- let str = &args[arg];
- if str.chars().nth(0)? != '-' {
- continue;
- }
-
- let arg = args.get(arg + 1)?;
- let value = if let Some(parse_value) = Config::value(arg.to_string()) {
- parse_value
- } else {
- arg.to_string()
- };
-
- match str.as_str() {
- "-host" => host.clone_from(&value),
- "-port" => port.clone_from(&value),
- "-blz_file" => manager_file.clone_from(&value),
- "-password" => password.clone_from(&value),
- _ => (),
- }
- }
- Some(Config {
- host,
- port,
- manager_file,
- password,
- })
- }
-
- fn default() -> Self {
- Config {
- host: "localhost".to_string(),
- port: "3306".to_string(),
- manager_file: "./db/datablaze/manage.blz".to_string(),
- password: "password".to_string(),
- }
- }
-}
diff --git a/src/server/headers.rs b/src/server/header_parsing.rs
similarity index 100%
rename from src/server/headers.rs
rename to src/server/header_parsing.rs
diff --git a/src/server/instance.rs b/src/server/instance.rs
new file mode 100644
index 0000000..f10fc15
--- /dev/null
+++ b/src/server/instance.rs
@@ -0,0 +1,170 @@
+use chrono::Local;
+use colored::Colorize;
+use ctrlc;
+
+use super::{client_session::ClientSession, header_parsing, runtime_config::RuntimeConfig};
+use crate::prelude::*;
+use crate::routine::{formatting::MessagesFormatting, info_channel::InfoChannel};
+use std::{
+ io::{self, Read, Result, Write},
+ net::TcpListener,
+ path::Path,
+ process::exit,
+ thread,
+ time::Duration,
+};
+
+pub struct ServerInstance {
+ info_channel: InfoChannel,
+ sessions: Vec,
+ config: RuntimeConfig,
+}
+
+impl ServerInstance {
+ pub fn new(info_channel: InfoChannel) -> Self {
+ Self {
+ info_channel,
+ sessions: vec![],
+ config: RuntimeConfig::default(),
+ }
+ }
+
+ pub async fn launch(&mut self, args: &[String]) -> Result<()> {
+ self.config = RuntimeConfig::parse_arguments(args.to_vec()).unwrap();
+ let error_marking = "Server Error".bright_red();
+ if !RuntimeConfig::blz_exists(&self.config.manage_file) {
+ let error_message = format!(
+ "{}: {} not found; couldn't raise a server{}",
+ error_marking,
+ &self.config.manage_file,
+ if args.is_empty() && self.config.manage_file == "main.manage.blz" {
+ "\n".to_owned()
+ + &String::from("Try to specify a path to the management file").into_hint()
+ } else {
+ "".to_string()
+ },
+ );
+ return Err(io::Error::new(ErrorKind::NotFound, error_message));
+ }
+ let listener = match TcpListener::bind(&self.config.address) {
+ Ok(success) => {
+ self.info_channel
+ .clone()
+ .send(format!(
+ "Gateway opened on {}",
+ self.config.address.yellow()
+ ))
+ .unwrap();
+ success
+ }
+ Err(error) => {
+ return Err(io::Error::new(
+ error.kind(),
+ format!(
+ "{}: {}; could not start",
+ error_marking,
+ match error.kind() {
+ ErrorKind::AddrInUse => {
+ "The address is already taken".to_string()
+ }
+ ErrorKind::PermissionDenied => {
+ "An attempt to bind the port was refused".to_string()
+ }
+ ErrorKind::InvalidInput => {
+ format!("{}: Invalid socket address", error_marking)
+ }
+ _ => {
+ format!("{}: {}", error_marking, error)
+ }
+ }
+ ),
+ ));
+ }
+ };
+
+ let info_channel = self.info_channel.clone();
+
+ info_channel
+ .clone()
+ .send(format!(
+ "Executing {}",
+ Path::new(&self.config.manage_file)
+ .file_name()
+ .unwrap()
+ .to_string_lossy()
+ .yellow()
+ ))
+ .unwrap();
+
+ ctrlc::set_handler(move || {
+ info_channel
+ .clone()
+ .send("\nInterrupted with ^C, shutdown".to_string())
+ .unwrap();
+ exit(0);
+ })
+ .expect("Error occured while setting ctrl+c handler");
+
+ self.start_closed_sessions_cleaner().await;
+ self.handle_connections(listener).await?;
+ Ok(())
+ }
+
+ async fn start_closed_sessions_cleaner(&mut self) -> tokio::task::JoinHandle<()> {
+ unsafe {
+ let this = &mut *(self as *mut Self);
+ tokio::spawn(async move {
+ loop {
+ thread::sleep(Duration::from_secs(10));
+ this.sessions
+ .retain(|session: &ClientSession| session.is_active());
+ }
+ })
+ }
+ }
+
+ async fn handle_connections(&mut self, listener: TcpListener) -> Result<()> {
+ for stream in listener.incoming() {
+ let mut stream = stream.unwrap();
+ let correct_password = &self.config.password;
+
+ let mut buffer = [0; 1024];
+ let mut request = String::new();
+
+ let bytesize = stream.read(&mut buffer).unwrap();
+ request.push_str(&String::from_utf8_lossy(&buffer[..bytesize]));
+
+ let header = header_parsing::parse_header(request.clone()).unwrap();
+
+ let client_password = header.get("password");
+ if client_password.is_none() || correct_password != client_password.unwrap() {
+ stream.write_all(b"HTTP/1.1 401 Unauthorized - Incorrect password\r\n\n")?;
+ continue;
+ } else if self.sessions.len() + 1 > self.config.sessions_limit as usize {
+ stream.write_all(b"HTTP/1.1 503 Sessions Limit\r\n\n")?;
+ }
+ let mut new_session = ClientSession::new(Local::now());
+ new_session.activate_temporairly(self.config.session_lifetime);
+ let session_id = new_session.id;
+ self.sessions.push(new_session);
+ let response_content = format!("{}", session_id);
+
+ self.info_channel
+ .clone()
+ .send("A new session been created".to_string())
+ .unwrap();
+
+ stream
+ .write_all(
+ format!(
+ "HTTP/1.1 200 Ok\r\nContent-Type: text/plain\r\nContent-Length: {}\r\n\n{}",
+ response_content.len(),
+ response_content
+ )
+ .as_bytes(),
+ )
+ .unwrap();
+ }
+ Ok(())
+ }
+}
diff --git a/src/server/mod.rs b/src/server/mod.rs
index 910fab1..bd33a96 100644
--- a/src/server/mod.rs
+++ b/src/server/mod.rs
@@ -1,3 +1,4 @@
-pub mod config;
-pub mod headers;
-pub mod server_bz;
+pub mod client_session;
+pub mod header_parsing;
+pub mod instance;
+pub mod runtime_config;
diff --git a/src/server/runtime_config.rs b/src/server/runtime_config.rs
new file mode 100644
index 0000000..2565b4e
--- /dev/null
+++ b/src/server/runtime_config.rs
@@ -0,0 +1,126 @@
+use colored::Colorize;
+use dotenvy::dotenv;
+use regex::Regex;
+use std::{
+ env,
+ ffi::OsStr,
+ io::{self, Result},
+ path::Path,
+ time::Duration,
+};
+
+pub struct RuntimeConfig {
+ pub address: String,
+ pub manage_file: String,
+ pub password: String,
+ pub sessions_limit: u32,
+ pub session_lifetime: Duration,
+}
+
+impl Default for RuntimeConfig {
+ fn default() -> Self {
+ Self {
+ address: "localhost:3306".to_string(),
+ manage_file: "./main.manage.blz".to_string(),
+ password: "password".to_string(),
+ sessions_limit: 100,
+ session_lifetime: Duration::from_secs(30),
+ }
+ }
+}
+
+impl RuntimeConfig {
+ fn get_env_value(user_key: String) -> Option {
+ dotenv().expect("Environment file couldn't be found");
+
+ let user_key = Regex::new(r"env\.(\w+)")
+ .unwrap()
+ .captures(&user_key)?
+ .get(1)?
+ .as_str();
+
+ env::vars()
+ .filter(|(key, _)| *key == user_key)
+ .map(|(_, value)| value)
+ .next()
+ }
+
+ pub fn blz_exists(path: &String) -> bool {
+ let blz_file = Path::new(&path);
+
+ if !blz_file.exists() || blz_file.extension().unwrap_or(OsStr::new("")) != "blz" {
+ return false;
+ }
+ true
+ }
+
+ pub fn parse_arguments(console_args: Vec) -> Result {
+ let default = Self::default();
+ let mut address = default.address;
+ let mut password = default.password;
+ let mut manage_file: String = "main.manage.blz".to_string();
+ let mut sessions_limit: u32 = default.sessions_limit;
+ let mut session_lifetime: Duration = default.session_lifetime;
+
+ let manage_file_fullname_regex = Regex::new(r".*\.manage\.blz$").unwrap();
+
+ for console_arg_index in 0..console_args.len() {
+ let argument_keyword = &console_args[console_arg_index];
+ if argument_keyword.chars().nth(0).unwrap() != '-' {
+ if console_arg_index == 0 {
+ manage_file = if !manage_file_fullname_regex.is_match(argument_keyword) {
+ format!("{}.manage.blz", argument_keyword)
+ } else {
+ argument_keyword.to_string()
+ };
+ };
+ continue;
+ }
+
+ let argument_value = match console_args.get(console_arg_index + 1) {
+ Some(value) => {
+ if let Some(env_value) =
+ RuntimeConfig::get_env_value(argument_keyword.to_string())
+ {
+ env_value
+ } else {
+ value.to_string()
+ }
+ }
+ None => {
+ return Err(io::Error::new(
+ io::ErrorKind::NotFound,
+ format!(
+ "{}: The value for '{}' argument isn't provided",
+ "Input Error".bright_red(),
+ &argument_keyword[1..argument_keyword.len()]
+ ),
+ ));
+ }
+ };
+
+ match argument_keyword.as_str() {
+ "-address" => address.clone_from(&argument_value),
+ "-password" => password.clone_from(&argument_value),
+ "-session_lifetime" => session_lifetime.clone_from(&Duration::from_secs(
+ argument_value
+ .parse()
+ .expect("Provide the right amount of seconds a session lives"),
+ )),
+ "-sessions_limit" => sessions_limit.clone_from(
+ &argument_value
+ .parse()
+ .expect("Unable to parse 'sessions_limit' field due to the incorrect type"),
+ ),
+ _ => (),
+ }
+ }
+ Ok(RuntimeConfig {
+ address,
+ manage_file,
+ password,
+ sessions_limit,
+ session_lifetime,
+ })
+ }
+}
diff --git a/src/server/server_bz.rs b/src/server/server_bz.rs
deleted file mode 100644
index b5bdb9b..0000000
--- a/src/server/server_bz.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-use crate::{
- server::{config::Config, headers},
- shell::handling::analyze_syntatically,
-};
-use std::io::{self, Read};
-use std::net::{TcpListener, TcpStream};
-
-pub fn server_run(args: Vec) -> io::Result<()> {
- let config = Config::parse_arguments(args).unwrap();
-
- if !Config::blz_exists(&config.manager_file) {
- return Err(io::Error::new(
- io::ErrorKind::NotFound,
- "blz_file not found",
- ));
- }
-
- let host = format!("{}:{}", config.host, config.port);
- let listener = TcpListener::bind(host)?;
-
- for stream in listener.incoming() {
- let stream = stream?;
- let password = std::mem::take(&mut config.password.clone());
-
- std::thread::spawn(move || handle_connection(stream, password));
- }
-
- Ok(())
-}
-
-fn handle_connection(mut stream: TcpStream, password: String) -> io::Result<()> {
- let mut buffer = [0; 1024];
- let mut request = String::new();
- let bytes_read = stream.read(&mut buffer)?;
- request.push_str(&String::from_utf8_lossy(&buffer[..bytes_read]));
-
- let header = headers::parse_header(request.clone()).unwrap();
- if let Some(value) = header.get("Password") {
- if password == *value {
- analyze_syntatically(headers::remove_empty_line(request).unwrap())?
- };
- };
-
- Ok(())
-}
diff --git a/src/shell/command_handler.rs b/src/shell/command_handler.rs
new file mode 100644
index 0000000..6b8790c
--- /dev/null
+++ b/src/shell/command_handler.rs
@@ -0,0 +1,158 @@
+use crate::routine::formatting::MessagesFormatting;
+use crate::routine::info_channel::{get_console_info_channel, InfoChannel};
+use crate::scripting::{ast::tokens::Token, lexer, parser};
+use crate::server::instance::ServerInstance;
+use colored::Colorize;
+use std::fs::{self, File};
+use std::io::{self, Result, Write};
+
+pub const OFFICIAL_REPOSITORY: &str = "https://github.com/Reeliks/blaze";
+
+pub struct ShellCommandHandler {
+ info_channel: InfoChannel,
+}
+
+impl Default for ShellCommandHandler {
+ fn default() -> Self {
+ Self {
+ info_channel: get_console_info_channel(),
+ }
+ }
+}
+
+impl ShellCommandHandler {
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ pub async fn handle_command(&mut self) -> Result<()> {
+ let mut args: Vec = std::env::args().collect::>()[1..].to_vec();
+ if args.is_empty() {
+ args.push("help".to_string());
+ };
+ let command = args[0].as_str();
+ match command {
+ "init" => {
+ let mut manage_file_name = "main";
+ if let Some(first_arg) = args.get(1) {
+ if !first_arg.starts_with('-') {
+ manage_file_name = first_arg
+ }
+ }
+
+ if Self::create_management_file(manage_file_name) {
+ println!("Thanks for using {}!", "Blaze Database".yellow());
+ println!(" * To contribute the development process, check out the official repository:\n {}", OFFICIAL_REPOSITORY);
+ } else {
+ println!(
+ "{}: The file/folder with a name '{}' already exists\n{}",
+ "Init Error".bright_red(),
+ manage_file_name,
+ String::from("Try specifying another name").into_hint()
+ );
+ }
+ }
+ "run" | "raise" => self.run_server(args).await?,
+ "lexer" => {
+ let text = Self::read_console_input()?;
+ self.run_lexer(text, true)?;
+ }
+ "parser" => {
+ let text = Self::read_console_input()?;
+ self.run_parser(text)?;
+ }
+ "--help" | "help" => Self::print_help_section(),
+ _ => {
+ eprintln!("'{}' is not a blaze commmand. See 'blaze --help'", command);
+ std::process::exit(1);
+ }
+ }
+ Ok(())
+ }
+
+ async fn run_server(&mut self, args: Vec) -> Result<()> {
+ if let Err(error) = ServerInstance::new(self.info_channel.clone())
+ .launch(&args[1..])
+ .await
+ {
+ eprintln!("{}", error);
+ };
+ Ok(())
+ }
+
+ // Do not indent raw strings below"
+ fn print_help_section() {
+ let help_list = r#">> Blaze Database 0.0.1a - available commands:
+Datablaze Management
+ init - create a new datablaze template to start working blazingly fast
+ run - raise a datablaze configurated in manage.blz | raise
+ * manage_file_name, -cons_limit, con_lifetime (secs), -address, -password
+
+Blaze Language (Dev)
+ lexer - tokens parsing
+ parser - nodes parsing (lexing included)
+"#;
+
+ println!("{}", help_list);
+ }
+
+ pub fn create_management_file(name: &str) -> bool {
+ let manage_file_content = br#"manage (
+ // address = "localhost:3306",
+ // sessions_limit = 100,
+ // session_lifetime = 60
+);
+
+attach "data";"#;
+
+ let manage_file_fullname = &format!(
+ "{}.manage.blz",
+ if !name.is_empty() { name } else { "main" }
+ );
+ if fs::metadata(manage_file_fullname).is_err() {
+ let mut manage_file = File::create(manage_file_fullname).unwrap();
+ manage_file.write_all(manage_file_content).unwrap();
+ return true;
+ };
+ false
+ }
+
+ pub fn run_lexer(&self, code_to_parse: String, show_final_message: bool) -> Result> {
+ let mut code_lexer = lexer::Lexer::new(code_to_parse, self.info_channel.clone());
+ code_lexer
+ .get_context()
+ .set_code_source("Shell".to_string());
+ let tokens = code_lexer.analyze()?;
+
+ if !tokens.is_empty() && show_final_message {
+ self.info_channel.clone().send(format!(
+ "Lexiical Analysis successfully completed! Tokens count: {}",
+ tokens.len()
+ ))?;
+ }
+ Ok(tokens)
+ }
+
+ pub fn run_parser(&self, code: String) -> Result<()> {
+ let tokens = self.run_lexer(code, false)?;
+ let mut code_parser = parser::Parser::new(tokens, self.info_channel.clone());
+ code_parser
+ .get_context()
+ .set_code_source("Shell".to_string());
+ let nodes = code_parser.parse()?.nodes;
+ if !nodes.is_empty() {
+ self.info_channel.clone().send(format!(
+ "Parsing successfully completed! Nodes Count: {}",
+ nodes.len()
+ ))?;
+ }
+
+ Ok(())
+ }
+
+ fn read_console_input() -> io::Result {
+ let mut text = String::new();
+ std::io::stdin().read_line(&mut text)?;
+ Ok(text.trim().to_string())
+ }
+}
diff --git a/src/shell/handling.rs b/src/shell/handling.rs
deleted file mode 100644
index d7ea8a7..0000000
--- a/src/shell/handling.rs
+++ /dev/null
@@ -1,104 +0,0 @@
-use crate::db::create_db;
-use crate::fs::filesystem::Fs;
-use crate::scripting::tokens::Token;
-use crate::scripting::{lexer, parser};
-use crate::server::server_bz;
-use serde::{Deserialize, Serialize};
-use std::io::{self, Result};
-
-#[derive(Debug, Serialize, Deserialize)]
-struct Person {
- name: String,
- age: i32,
- email: Option,
-}
-
-pub fn handle_command_arguments() -> Result<()> {
- let args: Vec = std::env::args().collect();
- if args.len() < 2 {
- print_help_section();
- return Ok(());
- };
- match args[1].as_str() {
- "create" => create_db_with_console()?,
- "run" => server_bz::server_run(args)?,
- "test" => {
- let person = Person {
- name: "SVD".to_string(),
- age: 30,
- email: Some("SVD@example.com".to_string()),
- };
-
- let bson_doc = bson::to_document(&person).unwrap();
-
- Fs::db_open("./test.bson".to_string())
- .unwrap()
- .db_write("Usser".to_string(), bson_doc)
- .unwrap();
- }
- "lexer" => {
- let text = input_text()?;
- analyze_lexically(text)?;
- }
- "parser" => {
- let text = input_text()?;
- analyze_syntatically(text)?;
- }
- _ => {
- eprintln!("Invalid arguments");
- std::process::exit(1);
- }
- }
- Ok(())
-}
-
-fn print_help_section() {
- let help_list = r#"Blaze Db 0.0.1a - available commands:
- Database management
- create - create a new datablaze
- Blaze Language
- lexer - get to see how the code is subjected to lexical analysis under the hood
- parser - try the first version of a parser
- run - start server"#;
-
- println!("{}", help_list);
-}
-
-pub fn create_db_with_console() -> Result<()> {
- let mut path = String::new();
- println!("Specify a path to a datablaze");
- io::stdin().read_line(&mut path)?;
- create_db::create_db_structure(path.trim())?;
-
- Ok(())
-}
-
-fn analyze_lexically(code_to_parse: String) -> Result> {
- let mut code_lexer = lexer::Lexer::new(code_to_parse);
- code_lexer
- .get_context()
- .set_code_source("Shell".to_string());
- Ok(code_lexer.analyze().unwrap())
-}
-
-pub fn analyze_syntatically(code: String) -> Result<()> {
- let tokens = analyze_lexically(code)?;
- let mut code_parser = parser::Parser::new(tokens);
- code_parser
- .get_context()
- .set_code_source("Shell".to_string());
- let nodes = code_parser.parse()?.nodes;
- if !nodes.is_empty() {
- println!(
- "Parsing successfully completed! Nodes Count: {}",
- nodes.len()
- );
- }
- Ok(())
-}
-
-fn input_text() -> io::Result {
- let mut code_to_parse = String::new();
- std::io::stdin().read_line(&mut code_to_parse)?;
- Ok(code_to_parse)
-}
diff --git a/src/shell/mod.rs b/src/shell/mod.rs
index 17af089..d86bd4d 100644
--- a/src/shell/mod.rs
+++ b/src/shell/mod.rs
@@ -1 +1 @@
-pub mod handling;
+pub mod command_handler;
diff --git a/test.bson b/test.bson
index 057128e..c796f81 100644
Binary files a/test.bson and b/test.bson differ
diff --git a/tests/test.rs b/tests/test.rs
index b510a2c..fe75d4c 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -1,8 +1,8 @@
-use blaze::db::create_db;
-use blaze::scripting::lexer::Lexer;
-use blaze::scripting::parser::Parser;
-use blaze::scripting::tokens::TokenType;
-use blaze::server::headers;
+use blaze::{
+ routine::info_channel::get_console_info_channel,
+ scripting::{ast::tokens::TokenType, lexer::Lexer, parser::Parser},
+ server::header_parsing::parse_header,
+};
#[test]
fn test_lexer() {
@@ -14,7 +14,7 @@ fn test_lexer() {
TokenType::Else,
];
- let code_lexer = Lexer::new(code_to_parse);
+ let code_lexer = Lexer::new(code_to_parse, get_console_info_channel());
let tokens = code_lexer.analyze().unwrap();
let actual_token_types: Vec = tokens
@@ -26,12 +26,12 @@ fn test_lexer() {
}
fn parser(code: &str) -> std::io::Result {
- let mut code_lexer = Lexer::new(code.to_string());
+ let mut code_lexer = Lexer::new(code.to_string(), get_console_info_channel());
let code_source = String::from("Tests");
code_lexer.get_context().code_source = code_source.clone();
let tokens = code_lexer.analyze()?;
- let mut code_parser = Parser::new(tokens.clone());
+ let mut code_parser = Parser::new(tokens.clone(), get_console_info_channel());
code_parser.get_context().code_source = code_source;
let ast = code_parser.parse();
@@ -53,18 +53,12 @@ fn test_parser() {
.unwrap());
}
-#[test]
-fn test_cteate_db() {
- let is_create = create_db::create_db_structure("./db".trim()).is_ok();
- assert!(is_create);
-}
-
#[test]
fn test_header_parser() {
let response = "POST / HTTP/1.1\nHost: localhost:3300\nUser-Agent: curl/8.7.1\nAccept: */*\nPassword: 1221\n"
.to_string();
- let hashmap = headers::parse_header(response).unwrap();
+ let hashmap = parse_header(response).unwrap();
if let Some(value) = hashmap.get("Password") {
assert!(value == "1221");
}