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()
- }
-}