From 4fd34f973fdd5cfa76b6d979fd34555e7b21be29 Mon Sep 17 00:00:00 2001 From: kjuulh Date: Sat, 2 Apr 2022 21:58:23 +0200 Subject: [PATCH] Add test --- .idea/runConfigurations/Test.xml | 18 + .idea/sqlite_clone.iml | 2 + Cargo.lock | 541 +++++++++++++++++++- Cargo.toml | 15 +- sqlite_bin/Cargo.toml | 9 + sqlite_bin/src/main.rs | 9 + sqlite_core/Cargo.toml | 14 + sqlite_core/src/app.rs | 71 +++ sqlite_core/src/input_buffer.rs | 92 ++++ sqlite_core/src/interaction/buffer.rs | 33 ++ sqlite_core/src/interaction/command_line.rs | 26 + sqlite_core/src/interaction/mod.rs | 7 + sqlite_core/src/lib.rs | 8 + {src => sqlite_core/src}/page.rs | 4 +- {src => sqlite_core/src}/pager.rs | 0 {src => sqlite_core/src}/row.rs | 18 +- {src => sqlite_core/src}/statement.rs | 29 +- {src => sqlite_core/src}/table.rs | 8 +- sqlite_core/tests/acceptance_test.rs | 118 +++++ src/main.rs | 98 ---- 20 files changed, 987 insertions(+), 133 deletions(-) create mode 100644 .idea/runConfigurations/Test.xml create mode 100644 sqlite_bin/Cargo.toml create mode 100644 sqlite_bin/src/main.rs create mode 100644 sqlite_core/Cargo.toml create mode 100644 sqlite_core/src/app.rs create mode 100644 sqlite_core/src/input_buffer.rs create mode 100644 sqlite_core/src/interaction/buffer.rs create mode 100644 sqlite_core/src/interaction/command_line.rs create mode 100644 sqlite_core/src/interaction/mod.rs create mode 100644 sqlite_core/src/lib.rs rename {src => sqlite_core/src}/page.rs (89%) rename {src => sqlite_core/src}/pager.rs (100%) rename {src => sqlite_core/src}/row.rs (89%) rename {src => sqlite_core/src}/statement.rs (74%) rename {src => sqlite_core/src}/table.rs (93%) create mode 100644 sqlite_core/tests/acceptance_test.rs delete mode 100644 src/main.rs diff --git a/.idea/runConfigurations/Test.xml b/.idea/runConfigurations/Test.xml new file mode 100644 index 0000000..15edb2f --- /dev/null +++ b/.idea/runConfigurations/Test.xml @@ -0,0 +1,18 @@ + + + + \ No newline at end of file diff --git a/.idea/sqlite_clone.iml b/.idea/sqlite_clone.iml index c254557..4ff02e9 100644 --- a/.idea/sqlite_clone.iml +++ b/.idea/sqlite_clone.iml @@ -2,6 +2,8 @@ + + diff --git a/Cargo.lock b/Cargo.lock index 8a45219..16a1675 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,29 @@ dependencies = [ "memchr", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base-x" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" + [[package]] name = "bincode" version = "2.0.0-rc.1" @@ -29,6 +52,35 @@ dependencies = [ "virtue", ] +[[package]] +name = "bumpalo" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" + +[[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.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +dependencies = [ + "atty", + "lazy_static", + "winapi", +] + +[[package]] +name = "const_fn" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" + [[package]] name = "const_format" version = "0.2.22" @@ -49,18 +101,213 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +dependencies = [ + "cfg-if", + "lazy_static", +] + +[[package]] +name = "darling" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "derive-new" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_builder" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2658621297f2cf68762a6f7dc0bb7e1ff2cfd6583daef8ee0fed6f7ec468ec0" +dependencies = [ + "darling", + "derive_builder_core", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_builder_core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2791ea3e372c8495c0bc2033991d76b512cd799d07491fbd6890124db9458bef" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "discard" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + [[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.121" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" + +[[package]] +name = "log" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +dependencies = [ + "cfg-if", +] + [[package]] name = "memchr" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + [[package]] name = "proc-macro2" version = "1.0.36" @@ -79,6 +326,31 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rayon" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + [[package]] name = "regex" version = "1.5.5" @@ -96,6 +368,55 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +[[package]] +name = "rspec" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89389e7c690310e855df3d9b507985ca0d323e2e766b2fedf369b02671e70e0a" +dependencies = [ + "colored", + "derive-new", + "derive_builder", + "rayon", + "time", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "serde" version = "1.0.136" @@ -117,10 +438,44 @@ dependencies = [ ] [[package]] -name = "sqlite_clone" +name = "serde_json" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +dependencies = [ + "sha1_smol", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + +[[package]] +name = "sqlite_bin" +version = "0.1.0" +dependencies = [ + "sqlite_core", +] + +[[package]] +name = "sqlite_core" version = "0.1.0" dependencies = [ "bincode", + "rspec", "serde", "sscanf", ] @@ -149,6 +504,70 @@ dependencies = [ "syn", ] +[[package]] +name = "standback" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" +dependencies = [ + "version_check", +] + +[[package]] +name = "stdweb" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" +dependencies = [ + "discard", + "rustc_version", + "stdweb-derive", + "stdweb-internal-macros", + "stdweb-internal-runtime", + "wasm-bindgen", +] + +[[package]] +name = "stdweb-derive" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_derive", + "syn", +] + +[[package]] +name = "stdweb-internal-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" +dependencies = [ + "base-x", + "proc-macro2", + "quote", + "serde", + "serde_derive", + "serde_json", + "sha1", + "syn", +] + +[[package]] +name = "stdweb-internal-runtime" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" + +[[package]] +name = "strsim" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" + [[package]] name = "syn" version = "1.0.89" @@ -160,14 +579,134 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "time" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" +dependencies = [ + "const_fn", + "libc", + "standback", + "stdweb", + "time-macros", + "version_check", + "winapi", +] + +[[package]] +name = "time-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" +dependencies = [ + "proc-macro-hack", + "time-macros-impl", +] + +[[package]] +name = "time-macros-impl" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "standback", + "syn", +] + [[package]] name = "unicode-xid" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "virtue" version = "0.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "757cfbfe0d17ee6f22fe97e536d463047d451b47cf9d11e2b7d1398b0ef274dd" + +[[package]] +name = "wasm-bindgen" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index c9ec48a..47ccf1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,6 @@ -[package] -name = "sqlite_clone" -version = "0.1.0" -edition = "2021" +[workspace] -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -sscanf = {version="0.2.1"} -bincode = "2.0.0-rc.1" -serde = {version="1.0.136", features=["derive"]} +members = [ + "sqlite_core", + "sqlite_bin" +] diff --git a/sqlite_bin/Cargo.toml b/sqlite_bin/Cargo.toml new file mode 100644 index 0000000..0520c03 --- /dev/null +++ b/sqlite_bin/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "sqlite_bin" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +sqlite_core = { path = "../sqlite_core" } \ No newline at end of file diff --git a/sqlite_bin/src/main.rs b/sqlite_bin/src/main.rs new file mode 100644 index 0000000..4e41328 --- /dev/null +++ b/sqlite_bin/src/main.rs @@ -0,0 +1,9 @@ +use sqlite_core::app::App; +use std::process::exit; + +fn main() -> Result<(), String> { + App::new() + .with_commandline_interaction() + .with_on_exit_handler(|| exit(0)) + .run() +} diff --git a/sqlite_core/Cargo.toml b/sqlite_core/Cargo.toml new file mode 100644 index 0000000..1d2f172 --- /dev/null +++ b/sqlite_core/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "sqlite_core" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +sscanf = { version = "0.2.1" } +bincode = "2.0.0-rc.1" +serde = { version = "1.0.136", features = ["derive"] } + +[dev_dependencies] +rspec = "1.0" \ No newline at end of file diff --git a/sqlite_core/src/app.rs b/sqlite_core/src/app.rs new file mode 100644 index 0000000..7701ef2 --- /dev/null +++ b/sqlite_core/src/app.rs @@ -0,0 +1,71 @@ +use crate::input_buffer::InputBuffer; +use crate::interaction::{buffer::Buffer, command_line::CommandLine, Interaction}; +use std::fmt::{Debug, Formatter}; +use std::sync::{Arc, Mutex}; + +pub struct App { + interaction: Option>, + on_exit: fn(), +} + +impl Default for App { + fn default() -> Self { + App::new() + } +} + +impl Debug for App { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("App").finish() + } +} + +impl App { + pub fn new() -> Self { + Self { + interaction: None, + on_exit: || {}, + } + } + + pub fn with_interaction( + &mut self, + interaction: Arc, + ) -> &mut App { + self.interaction = Some(interaction); + self + } + + pub fn with_commandline_interaction(&mut self) -> &mut Self { + self.with_interaction(Arc::new(CommandLine::new())) + } + + pub fn with_buffer_interaction( + &mut self, + in_buffer: Arc>>, + out_buffer: Arc>>, + ) -> &mut Self { + self.with_interaction(Arc::new(Buffer::new(in_buffer, out_buffer))) + } + + pub fn with_on_exit_handler(&mut self, on_exit: fn()) -> &mut Self { + self.on_exit = on_exit; + + self + } + + pub fn run(&mut self) -> Result<(), String> { + if let None = self.interaction { + return Err(String::from("missing interaction")); + } + + let mut input_buffer = InputBuffer::new(self.interaction.take().unwrap()); + + loop { + input_buffer + .print_prompt() + .read_input()? + .parse(self.on_exit); + } + } +} diff --git a/sqlite_core/src/input_buffer.rs b/sqlite_core/src/input_buffer.rs new file mode 100644 index 0000000..7f74945 --- /dev/null +++ b/sqlite_core/src/input_buffer.rs @@ -0,0 +1,92 @@ +use crate::interaction::Interaction; +use crate::statement::{ExecuteResult, StatementResultType}; +use crate::table::Table; +use crate::{interaction, statement}; +use std::ops::Deref; +use std::sync::Arc; + +pub struct InputBuffer { + buffer: Option, + table: Table, + interaction: Arc, +} + +impl InputBuffer { + pub fn new(interaction: Arc) -> Self { + return Self { + buffer: None, + table: Table::new(), + interaction, + }; + } + + pub fn print_prompt(&mut self) -> &mut Self { + self.interaction.output(String::from("db > ")); + self + } + + pub fn read_input(&mut self) -> Result<&mut Self, String> { + self.buffer = Some(String::new()); + match &mut self.buffer { + Some(input_buffer) => { + if let Ok(input) = self.interaction.input() { + *input_buffer = input.to_string(); + Ok(self) + } else { + Err(String::from("could not handle input")) + } + } + _ => Err(String::from("Could not initialize buffer")), + } + } + + pub fn parse(&mut self, on_exit: fn() -> ()) { + match &self.buffer { + Some(command) => { + if command.starts_with(".") { + Self::handle_meta_statement(command, on_exit); + } else { + match Self::prepare_statement(&command.replace("\n", "")) { + Ok(statement) => { + let execution_result = statement.execute(&mut self.table); + match execution_result { + ExecuteResult::Success { + statement_result_type: StatementResultType::Insert, + } => self + .interaction + .deref() + .output(String::from("insert success\n")), + ExecuteResult::Success { + statement_result_type: StatementResultType::Select { rows }, + } => { + for row in rows { + self.interaction.output(row.print()) + } + self.interaction.output(String::from("select success\n")) + } + _ => self.interaction.output(String::from("failure\n")), + } + } + Err(e) => { + self.interaction.output(format!("{}\n", e)); + } + } + } + } + None => {} + } + } + + fn prepare_statement(command: &String) -> Result { + return statement::Statement::parse_statement(command); + } + + fn handle_meta_statement(command: &String, on_exit: fn()) { + match command.replace("\n", "").trim() { + ".exit" => on_exit(), + cmd => { + println!("Could not handle command: {cmd}") + } + } + } +} diff --git a/sqlite_core/src/interaction/buffer.rs b/sqlite_core/src/interaction/buffer.rs new file mode 100644 index 0000000..c6fb91e --- /dev/null +++ b/sqlite_core/src/interaction/buffer.rs @@ -0,0 +1,33 @@ +use std::sync::{Arc, Mutex}; + +use crate::interaction::Interaction; + +pub struct Buffer { + int_buffer: Arc>>, + out_buffer: Arc>>, +} + +impl Buffer { + pub fn new(int_buffer: Arc>>, out_buffer: Arc>>) -> Self { + Self { + int_buffer, + out_buffer, + } + } +} + +impl Interaction for Buffer { + fn input(&self) -> Result { + let mut int_buffer = self.int_buffer.lock().unwrap(); + if let Some(input_string) = int_buffer.pop() { + Ok(input_string) + } else { + Err(String::from("internal_buffer is empty")) + } + } + + fn output(&self, output: String) { + let mut out_buffer = self.out_buffer.lock().unwrap(); + out_buffer.push(output) + } +} diff --git a/sqlite_core/src/interaction/command_line.rs b/sqlite_core/src/interaction/command_line.rs new file mode 100644 index 0000000..65fa55d --- /dev/null +++ b/sqlite_core/src/interaction/command_line.rs @@ -0,0 +1,26 @@ +use crate::interaction::Interaction; +use std::io::Write; + +pub struct CommandLine {} + +impl CommandLine { + pub fn new() -> Self { + Self {} + } +} + +impl Interaction for CommandLine { + fn input(&self) -> Result { + let buffer: &mut String = &mut String::new(); + if let Ok(_) = std::io::stdin().read_line(buffer) { + return Ok(buffer.to_string()); + } else { + return Err(String::from("could not read input buffer")); + } + } + + fn output(&self, output: String) { + let _ = std::io::stdout().write(output.as_bytes()); + let _ = std::io::stdout().flush(); + } +} diff --git a/sqlite_core/src/interaction/mod.rs b/sqlite_core/src/interaction/mod.rs new file mode 100644 index 0000000..5682fce --- /dev/null +++ b/sqlite_core/src/interaction/mod.rs @@ -0,0 +1,7 @@ +pub mod buffer; +pub mod command_line; + +pub trait Interaction { + fn input(&self) -> Result; + fn output(&self, output: String); +} diff --git a/sqlite_core/src/lib.rs b/sqlite_core/src/lib.rs new file mode 100644 index 0000000..5762395 --- /dev/null +++ b/sqlite_core/src/lib.rs @@ -0,0 +1,8 @@ +pub mod app; +mod input_buffer; +mod interaction; +mod page; +mod pager; +mod row; +mod statement; +mod table; diff --git a/src/page.rs b/sqlite_core/src/page.rs similarity index 89% rename from src/page.rs rename to sqlite_core/src/page.rs index a17627b..d70dfd4 100644 --- a/src/page.rs +++ b/sqlite_core/src/page.rs @@ -7,9 +7,7 @@ pub struct Page { impl Page { pub fn new(buffer: Vec) -> Self { - Self { - buffer, - } + Self { buffer } } } diff --git a/src/pager.rs b/sqlite_core/src/pager.rs similarity index 100% rename from src/pager.rs rename to sqlite_core/src/pager.rs diff --git a/src/row.rs b/sqlite_core/src/row.rs similarity index 89% rename from src/row.rs rename to sqlite_core/src/row.rs index 3f41668..afbc4d2 100644 --- a/src/row.rs +++ b/sqlite_core/src/row.rs @@ -1,9 +1,9 @@ -use std::{iter, slice}; use std::io::Write; +use std::{iter, slice}; -use bincode::{Decode, Encode}; use bincode::config::Configuration; use bincode::error::{DecodeError, EncodeError}; +use bincode::{Decode, Encode}; const COLUMN_USERNAME_SIZE: usize = 32; const COLUMN_EMAIL_SIZE: usize = 255; @@ -18,8 +18,8 @@ pub struct Row { pub trait Serialize { fn serialize(&self) -> Result, EncodeError>; fn deserialize(buffer: &[u8]) -> Result - where - Self: Sized; + where + Self: Sized; } impl Serialize for Row { @@ -61,9 +61,13 @@ impl Row { } } - - pub(crate) fn print(&self) { - println!("({}, {}, {})", self.id, String::from_utf8(self.email.to_vec()).unwrap(), String::from_utf8(self.username.to_vec()).unwrap()) + pub fn print(&self) -> String { + format!( + "({}, {}, {})", + self.id, + String::from_utf8(self.email.to_vec()).unwrap(), + String::from_utf8(self.username.to_vec()).unwrap() + ) } } diff --git a/src/statement.rs b/sqlite_core/src/statement.rs similarity index 74% rename from src/statement.rs rename to sqlite_core/src/statement.rs index 820133e..1b2f85d 100644 --- a/src/statement.rs +++ b/sqlite_core/src/statement.rs @@ -1,16 +1,24 @@ +use crate::row; use crate::row::Serialize; use crate::table::Table; -use crate::{row}; - pub enum StatementType { Insert { row: row::Row }, Select, } +pub enum StatementResultType { + Insert, + Select { rows: Vec }, +} + pub enum ExecuteResult { - Error { error: String }, - Success, + Error { + error: String, + }, + Success { + statement_result_type: StatementResultType, + }, } pub struct Statement { @@ -44,7 +52,7 @@ impl Statement { } } - pub(crate) fn execute(&self, table: &mut Table) -> ExecuteResult { + pub fn execute(&self, table: &mut Table) -> ExecuteResult { match &self.statement_type { StatementType::Insert { row } => match row.serialize() { Err(e) => ExecuteResult::Error { @@ -52,16 +60,17 @@ impl Statement { }, Ok(serialization) => { table.append_row(serialization); - ExecuteResult::Success + ExecuteResult::Success { + statement_result_type: StatementResultType::Insert {}, + } } }, StatementType::Select => { let rows = table.get_rows(); - for row in rows { - row.print() - } - return ExecuteResult::Success; + return ExecuteResult::Success { + statement_result_type: StatementResultType::Select { rows }, + }; } } } diff --git a/src/table.rs b/sqlite_core/src/table.rs similarity index 93% rename from src/table.rs rename to sqlite_core/src/table.rs index 51ef4e9..9e2edb5 100644 --- a/src/table.rs +++ b/sqlite_core/src/table.rs @@ -1,9 +1,9 @@ use crate::pager::Pager; use crate::row; -use std::mem::size_of; -use std::ops::DerefMut; use crate::row::Row; use crate::row::Serialize; +use std::mem::size_of; +use std::ops::DerefMut; const PAGE_SIZE: usize = 4096; @@ -20,7 +20,7 @@ impl Table { } } - pub(crate) fn append_row(&mut self, row: Vec) { + pub fn append_row(&mut self, row: Vec) { let row_size = size_of::(); let page_num = self.num_rows / self.rows_per_page(row_size); let row_offset = self.num_rows % self.rows_per_page(row_size); @@ -50,7 +50,7 @@ impl Table { PAGE_SIZE / row_size } - pub(crate) fn get_rows(&mut self) -> Vec { + pub fn get_rows(&mut self) -> Vec { let mut rows: Vec = Vec::with_capacity(self.num_rows); for i in 0..self.num_rows { rows.push(self.row(i)) diff --git a/sqlite_core/tests/acceptance_test.rs b/sqlite_core/tests/acceptance_test.rs new file mode 100644 index 0000000..9df3c6b --- /dev/null +++ b/sqlite_core/tests/acceptance_test.rs @@ -0,0 +1,118 @@ +use std::ops::Deref; +use std::sync::{Arc, Mutex}; + +use sqlite_core::app::App; + +#[derive(Clone, Debug, Default)] +struct EmptyDatabaseEnv { + input: Arc>>, + output: Arc>>, + app: Arc>, +} + +#[test] +fn integration_test() { + let env = EmptyDatabaseEnv { + input: Arc::new(Mutex::new(Vec::new())), + output: Arc::new(Mutex::new(Vec::new())), + app: Arc::new(Mutex::new(App::new())), + }; + rspec::run(&rspec::given("a sqlite database", env, |ctx| { + ctx.before_each(|env| { + env.input = Arc::new(Mutex::new(Vec::new())); + env.output = Arc::new(Mutex::new(Vec::new())); + env.app = Arc::new(Mutex::new(App::new())); + }); + + ctx.when("it runs with empty items", |ctx| { + ctx.then("it returns input error", |value| { + let result = value + .app + .lock() + .as_mut() + .unwrap() + .with_buffer_interaction(value.input.clone(), value.output.clone()) + .run(); + assert_eq!(result, Err(String::from("could not handle input"))); + + assert_eq!(0, value.input.lock().unwrap().deref().len()); + assert_eq!(1, value.output.lock().unwrap().deref().len()); + assert_eq!( + String::from("db > "), + value.output.lock().unwrap().deref()[0] + ); + }) + }); + + ctx.when("it runs with insert items", |ctx| { + ctx.before_all(|value| { + let mut input = value.input.lock().unwrap(); + input.push(String::from("insert 1 username user@email.com")); + input.push(String::from("insert 2 username2 user2@email.com")); + input.push(String::from(".exit")); + input.reverse(); + }); + + ctx.then("returns success", |value| { + let result = value + .app + .lock() + .as_mut() + .unwrap() + .with_buffer_interaction(value.input.clone(), value.output.clone()) + .run(); + assert_eq!(result, Err(String::from("could not handle input"))); + + assert_eq!(6, value.output.lock().unwrap().deref().len()); + assert_eq!( + String::from("db > "), + value.output.lock().unwrap().deref()[0] + ); + assert_eq!( + String::from("insert success\n"), + value.output.lock().unwrap().deref()[1] + ); + assert_eq!( + String::from("db > "), + value.output.lock().unwrap().deref()[2] + ); + assert_eq!( + String::from("insert success\n"), + value.output.lock().unwrap().deref()[3] + ); + assert_eq!( + String::from("db > "), + value.output.lock().unwrap().deref()[4] + ); + assert_eq!( + String::from("db > "), + value.output.lock().unwrap().deref()[5] + ); + }); + }); + + ctx.when("it runs with select items", |ctx| { + ctx.before_all(|value| { + let mut input = value.input.lock().unwrap(); + input.push(String::from("insert 1 username user@email.com")); + input.push(String::from("insert 2 username2 user2@email.com")); + input.push(String::from("select")); + input.push(String::from(".exit")); + input.reverse(); + }); + + ctx.then("returns success", |value| { + let result = value + .app + .lock() + .as_mut() + .unwrap() + .with_buffer_interaction(value.input.clone(), value.output.clone()) + .run(); + assert_eq!(result, Err(String::from("could not handle input"))); + + assert_eq!(10, value.output.lock().unwrap().deref().len()); + }); + }); + })) +} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 256614d..0000000 --- a/src/main.rs +++ /dev/null @@ -1,98 +0,0 @@ -mod page; -mod pager; -mod row; -mod statement; -mod table; - -use crate::statement::ExecuteResult; -use crate::table::Table; -use std::io; -use std::io::Write; -use std::process::exit; - -struct InputBuffer { - buffer: Option, - table: Table, -} - -impl InputBuffer { - pub fn new() -> Self { - return Self { - buffer: None, - table: Table::new(), - }; - } - - pub(crate) fn print_prompt(&mut self) -> &mut Self { - print!("db > "); - if let Err(_) = io::stdout().flush() { - panic!("could not print to stdout") - } - self - } - - pub(crate) fn read_input(&mut self) -> Result<&mut Self, String> { - self.buffer = Some(String::new()); - match &mut self.buffer { - Some(input_buffer) => { - if let Ok(_) = io::stdin().read_line(input_buffer) { - Ok(self) - } else { - Err(String::from("could not handle input")) - } - } - _ => Err(String::from("Could not initialize buffer")), - } - } - - pub(crate) fn parse(&mut self) { - match &self.buffer { - Some(command) => { - if command.starts_with(".") { - Self::handle_meta_statement(command); - } else { - match Self::prepare_statement(&command.replace("\n", "")) { - Ok(statement) => { - let execution_result = statement.execute(&mut self.table); - match execution_result { - ExecuteResult::Success => { - println!("success") - } - _ => { - println!("failure") - } - } - } - Err(e) => { - println!("{}", e); - } - } - } - } - None => {} - } - } - - fn prepare_statement(command: &String) -> Result { - return statement::Statement::parse_statement(command); - } - - fn handle_meta_statement(command: &String) { - match command.replace("\n", "").trim() { - ".exit" => { - exit(0); - } - cmd => { - println!("Could not handle command: {cmd}") - } - } - } -} - -fn main() -> Result<(), String> { - let mut input_buffer = InputBuffer::new(); - - loop { - input_buffer.print_prompt().read_input()?.parse() - } -}