Start implementing a cas in separate crates.
This commit is contained in:
297
Cargo.lock
generated
297
Cargo.lock
generated
@@ -2,62 +2,6 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "addr2line"
|
|
||||||
version = "0.15.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "03345e98af8f3d786b6d9f656ccfa6ac316d954e92bc4841f0bba20789d5fb5a"
|
|
||||||
dependencies = [
|
|
||||||
"gimli",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "adler"
|
|
||||||
version = "1.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ansi_term"
|
|
||||||
version = "0.11.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
|
||||||
dependencies = [
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[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.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "backtrace"
|
|
||||||
version = "0.3.59"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4717cfcbfaa661a0fd48f8453951837ae7e8f81e481fbb136e3202d72805a744"
|
|
||||||
dependencies = [
|
|
||||||
"addr2line",
|
|
||||||
"cc",
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"miniz_oxide",
|
|
||||||
"object",
|
|
||||||
"rustc-demangle",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
@@ -74,20 +18,24 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bsv"
|
name = "cas-core"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"digest",
|
||||||
"error-chain",
|
"sha2",
|
||||||
"hostname",
|
"thiserror",
|
||||||
"libbsv",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cas-simple"
|
||||||
version = "1.0.67"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
dependencies = [
|
||||||
checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
|
"cas-core",
|
||||||
|
"digest",
|
||||||
|
"sha2",
|
||||||
|
"tempfile",
|
||||||
|
"toml",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
@@ -95,39 +43,15 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "clap"
|
|
||||||
version = "2.33.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
|
||||||
dependencies = [
|
|
||||||
"ansi_term",
|
|
||||||
"atty",
|
|
||||||
"bitflags",
|
|
||||||
"strsim",
|
|
||||||
"textwrap",
|
|
||||||
"unicode-width",
|
|
||||||
"vec_map",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.1.1"
|
version = "0.1.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dec1028182c380cc45a2e2c5ec841134f2dfd0f8f5f0a5bcd68004f81b5efdf4"
|
checksum = "66c99696f6c9dd7f35d486b9d04d7e6e202aa3e8c40d553f2fdf5e7e0c6a71ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crc32fast"
|
|
||||||
version = "1.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
@@ -137,28 +61,6 @@ dependencies = [
|
|||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "error-chain"
|
|
||||||
version = "0.12.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc"
|
|
||||||
dependencies = [
|
|
||||||
"backtrace",
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "flate2"
|
|
||||||
version = "1.0.20"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"crc32fast",
|
|
||||||
"libc",
|
|
||||||
"miniz_oxide",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.14.4"
|
version = "0.14.4"
|
||||||
@@ -171,82 +73,20 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.2"
|
version = "0.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
|
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "gimli"
|
|
||||||
version = "0.24.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hermit-abi"
|
|
||||||
version = "0.1.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hostname"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"match_cfg",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libbsv"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"digest",
|
|
||||||
"error-chain",
|
|
||||||
"flate2",
|
|
||||||
"serde",
|
|
||||||
"sha2",
|
|
||||||
"tempfile",
|
|
||||||
"toml",
|
|
||||||
"uuid",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.94"
|
version = "0.2.98"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e"
|
checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "match_cfg"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "miniz_oxide"
|
|
||||||
version = "0.4.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
|
|
||||||
dependencies = [
|
|
||||||
"adler",
|
|
||||||
"autocfg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "object"
|
|
||||||
version = "0.24.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1a5b3dd1c072ee7963717671d1ca129f1048fda25edea6b752bfc71ac8854170"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opaque-debug"
|
name = "opaque-debug"
|
||||||
@@ -262,9 +102,9 @@ checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.26"
|
version = "1.0.27"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
|
checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
@@ -280,9 +120,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.3"
|
version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
|
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"rand_chacha",
|
"rand_chacha",
|
||||||
@@ -292,9 +132,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_chacha"
|
name = "rand_chacha"
|
||||||
version = "0.3.0"
|
version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ppv-lite86",
|
"ppv-lite86",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
@@ -302,27 +142,27 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_core"
|
name = "rand_core"
|
||||||
version = "0.6.2"
|
version = "0.6.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
|
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_hc"
|
name = "rand_hc"
|
||||||
version = "0.3.0"
|
version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
|
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rand_core",
|
"rand_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.2.8"
|
version = "0.2.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc"
|
checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
@@ -336,31 +176,11 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustc-demangle"
|
|
||||||
version = "0.1.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "410f7acf3cb3a44527c5d9546bad4bf4e6c460915d5f9f2fc524498bfe8f70ce"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.126"
|
version = "1.0.127"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
|
checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8"
|
||||||
dependencies = [
|
|
||||||
"serde_derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_derive"
|
|
||||||
version = "1.0.126"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha2"
|
name = "sha2"
|
||||||
@@ -375,17 +195,11 @@ dependencies = [
|
|||||||
"opaque-debug",
|
"opaque-debug",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "strsim"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.72"
|
version = "1.0.73"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82"
|
checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -407,12 +221,23 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "textwrap"
|
name = "thiserror"
|
||||||
version = "0.11.0"
|
version = "1.0.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-width",
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -430,34 +255,12 @@ version = "1.13.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
|
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-width"
|
|
||||||
version = "0.1.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "uuid"
|
|
||||||
version = "0.8.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
|
||||||
dependencies = [
|
|
||||||
"getrandom",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "vec_map"
|
|
||||||
version = "0.8.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.3"
|
version = "0.9.3"
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
|
|
||||||
members = [
|
members = [
|
||||||
"libbsv",
|
"cas-core",
|
||||||
"bsv",
|
"cas-simple",
|
||||||
|
# "libbsv",
|
||||||
|
# "bsv",
|
||||||
]
|
]
|
||||||
|
|||||||
13
cas-core/Cargo.toml
Normal file
13
cas-core/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "cas-core"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Simon Boyé <sim.boye@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
license = "AGPL-3.0-or-later"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
thiserror = "1.0.25"
|
||||||
|
digest = { version = "0.9.0", features = ["alloc"] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
sha2 = "0.9.5"
|
||||||
40
cas-core/src/cas.rs
Normal file
40
cas-core/src/cas.rs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// This file is part of bsv.
|
||||||
|
//
|
||||||
|
// bsv is free software: you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Affero General Public License as published by the Free
|
||||||
|
// Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
// any later version.
|
||||||
|
//
|
||||||
|
// cdb is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||||
|
// more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the Affero GNU General Public License
|
||||||
|
// along with cdb. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
use super::error::Result;
|
||||||
|
use super::object_id::ObjectId;
|
||||||
|
use super::object_type::ObjectType;
|
||||||
|
use super::object_metadata::ObjectMetadata;
|
||||||
|
|
||||||
|
|
||||||
|
// pub struct ObjectIdIterator;
|
||||||
|
|
||||||
|
|
||||||
|
pub trait Cas {
|
||||||
|
fn object_id_from_string(&self, hex: &str) -> Result<ObjectId>;
|
||||||
|
fn object_id_from_partial(&self, hex: &str) -> Result<ObjectId>;
|
||||||
|
|
||||||
|
fn has_object_id(&self, oid: &ObjectId) -> Result<bool>;
|
||||||
|
// fn iter_object_id(&self) -> Result<ObjectIdIterator>;
|
||||||
|
|
||||||
|
fn read_object(&self, oid: &ObjectId) -> Result<(ObjectMetadata, &dyn std::io::Read)>;
|
||||||
|
fn write_object(&mut self, otype: ObjectType, data: &mut dyn std::io::Read) -> Result<ObjectId>;
|
||||||
|
fn remove_object(&mut self, oid: &ObjectId) -> Result<()>;
|
||||||
|
|
||||||
|
fn read_ref(&self, key: &str) -> Result<ObjectId>;
|
||||||
|
fn write_ref(&mut self, key: &str, value: &ObjectId) -> Result<()>;
|
||||||
|
fn remove_ref(&mut self, key: &str) -> Result<()>;
|
||||||
|
}
|
||||||
122
cas-core/src/error.rs
Normal file
122
cas-core/src/error.rs
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
// This file is part of bsv.
|
||||||
|
//
|
||||||
|
// bsv is free software: you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Affero General Public License as published by the Free
|
||||||
|
// Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
// any later version.
|
||||||
|
//
|
||||||
|
// cdb is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||||
|
// more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the Affero GNU General Public License
|
||||||
|
// along with cdb. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
// use std::path::PathBuf;
|
||||||
|
|
||||||
|
|
||||||
|
/// Result type used through cas-core.
|
||||||
|
pub type Result<T> = std::result::Result<T, Box<Error>>;
|
||||||
|
|
||||||
|
/// Error type used through cas-core.
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||||
|
pub enum Error {
|
||||||
|
// #[error("failed to create repository: {message}")]
|
||||||
|
// RepositoryCreationFailed {
|
||||||
|
// message: String,
|
||||||
|
// source: Option<Box<dyn std::error::Error>>,
|
||||||
|
// },
|
||||||
|
|
||||||
|
// #[error("invalid object id: {message}")]
|
||||||
|
// InvalidObjectId {
|
||||||
|
// message: String,
|
||||||
|
// source: Option<Box<dyn std::error::Error>>,
|
||||||
|
// },
|
||||||
|
|
||||||
|
// #[error("invalid size (got: {size}, expected: {expected})")]
|
||||||
|
// InvalidSize {
|
||||||
|
// size: usize,
|
||||||
|
// expected: usize,
|
||||||
|
// },
|
||||||
|
|
||||||
|
// #[error("non-empty directory ({dir})")]
|
||||||
|
// NonEmptyDirectory {
|
||||||
|
// dir: PathBuf
|
||||||
|
// },
|
||||||
|
|
||||||
|
// #[error("invalid character(s) ({characters})")]
|
||||||
|
// InvalidCharacters {
|
||||||
|
// characters: String,
|
||||||
|
// },
|
||||||
|
|
||||||
|
// #[error("invalid object type ({otype:?})")]
|
||||||
|
// InvalidObjectType {
|
||||||
|
// otype: [u8; 4],
|
||||||
|
// },
|
||||||
|
|
||||||
|
// #[error("invalid object size (expected {expected}, got {size})")]
|
||||||
|
// InvalidObjectSize {
|
||||||
|
// size: u64,
|
||||||
|
// expected: u64,
|
||||||
|
// },
|
||||||
|
|
||||||
|
// #[error("unsupported file type")]
|
||||||
|
// UnsupportedFileType,
|
||||||
|
|
||||||
|
// #[error("invalid path ({path})")]
|
||||||
|
// InvalidPath { path: PathBuf },
|
||||||
|
|
||||||
|
// #[error("io error{}", format_optional_path(path))]
|
||||||
|
// IoError {
|
||||||
|
// source: std::io::Error,
|
||||||
|
// path: Option<PathBuf>,
|
||||||
|
// },
|
||||||
|
|
||||||
|
#[error("{0}")]
|
||||||
|
Error(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
// pub fn repository_creation_failed<M: Into<String>>(message: M) -> Box<Error> {
|
||||||
|
// Box::new(Error::RepositoryCreationFailed {
|
||||||
|
// message: message.into(),
|
||||||
|
// source: None,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn repository_creation_failed_from<M: Into<String>>(source: Box<dyn std::error::Error>, message: M) -> Box<Error> {
|
||||||
|
// Box::new(Error::RepositoryCreationFailed {
|
||||||
|
// message: message.into(),
|
||||||
|
// source: Some(source),
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn invalid_object_id<M: Into<String>>(message: M) -> Box<Error> {
|
||||||
|
// Box::new(Error::InvalidObjectId {
|
||||||
|
// message: message.into(),
|
||||||
|
// source: None,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn invalid_object_id_from<M: Into<String>>(source: Box<dyn std::error::Error>, message: M) -> Box<Error> {
|
||||||
|
// Box::new(Error::InvalidObjectId {
|
||||||
|
// message: message.into(),
|
||||||
|
// source: Some(source),
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub fn error<M: Into<String>>(message: M) -> Box<Error> {
|
||||||
|
Box::new(Error::Error(message.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// fn format_optional_path(maybe_path: &Option<PathBuf>) -> String {
|
||||||
|
// match maybe_path {
|
||||||
|
// Some(path) => { format!(" ({:?})", path) },
|
||||||
|
// None => { String::new() }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
44
cas-core/src/lib.rs
Normal file
44
cas-core/src/lib.rs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// This file is part of bsv.
|
||||||
|
//
|
||||||
|
// bsv is free software: you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Affero General Public License as published by the Free
|
||||||
|
// Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
// any later version.
|
||||||
|
//
|
||||||
|
// cdb is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||||
|
// more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the Affero GNU General Public License
|
||||||
|
// along with cdb. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! # cas-core
|
||||||
|
//!
|
||||||
|
//! `cas-core` provides traits and types to interface with content-addressable
|
||||||
|
//! storage.
|
||||||
|
|
||||||
|
#![feature(inherent_ascii_escape)]
|
||||||
|
|
||||||
|
|
||||||
|
extern crate thiserror;
|
||||||
|
extern crate digest;
|
||||||
|
|
||||||
|
|
||||||
|
mod error;
|
||||||
|
mod object_id;
|
||||||
|
mod object_type;
|
||||||
|
mod object_metadata;
|
||||||
|
mod pipeline;
|
||||||
|
mod cas;
|
||||||
|
|
||||||
|
|
||||||
|
pub use crate::{
|
||||||
|
error::{Error, Result},
|
||||||
|
object_id::{ObjectId, hex, write_hex},
|
||||||
|
object_type::{ObjectType},
|
||||||
|
object_metadata::{ObjectMetadata},
|
||||||
|
pipeline::{Pipeline, DefaultPipeline},
|
||||||
|
cas::{Cas},
|
||||||
|
};
|
||||||
|
|
||||||
212
cas-core/src/object_id.rs
Normal file
212
cas-core/src/object_id.rs
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
// This file is part of bsv.
|
||||||
|
//
|
||||||
|
// bsv is free software: you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Affero General Public License as published by the Free
|
||||||
|
// Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
// any later version.
|
||||||
|
//
|
||||||
|
// cdb is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||||
|
// more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the Affero GNU General Public License
|
||||||
|
// along with cdb. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
use std::str::{FromStr};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use super::error::*;
|
||||||
|
|
||||||
|
|
||||||
|
/// A unique identifier for an object.
|
||||||
|
///
|
||||||
|
/// This is the handle used to reference an Object. This is an opaque type that uniquely identify an
|
||||||
|
/// object. It can be compared to another ObjectId, be hashed and that's about it.
|
||||||
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct ObjectId {
|
||||||
|
id: Arc<Vec<u8>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectId {
|
||||||
|
pub fn new(id: &[u8]) -> ObjectId {
|
||||||
|
ObjectId {
|
||||||
|
id: Arc::new(id.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(&self) -> &[u8] {
|
||||||
|
self.id.as_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for ObjectId {
|
||||||
|
type Err = Box<Error>;
|
||||||
|
|
||||||
|
fn from_str(id_str: &str) -> Result<ObjectId> {
|
||||||
|
Ok(ObjectId {
|
||||||
|
id: Arc::new(
|
||||||
|
CharPairs::new(id_str, false)
|
||||||
|
.map(parse_byte)
|
||||||
|
.collect::<Result<Vec<_>>>()?
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ObjectId {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> {
|
||||||
|
write_hex(f, self.id.as_slice())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for ObjectId {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> {
|
||||||
|
write!(f, "ObjectId::new(\"{}\")", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn hex(bytes: &[u8]) -> String {
|
||||||
|
let mut hex = String::with_capacity(bytes.len() * 2);
|
||||||
|
write_hex(&mut hex, bytes).expect("can't format bytes ?");
|
||||||
|
hex
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_hex<W: std::fmt::Write>(write: &mut W, bytes: &[u8])
|
||||||
|
-> std::result::Result<(), std::fmt::Error>
|
||||||
|
{
|
||||||
|
for byte in bytes.iter() {
|
||||||
|
write!(write, "{:02x}", byte)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct CharPairs<'a> {
|
||||||
|
string: &'a str,
|
||||||
|
chars: std::str::CharIndices<'a>,
|
||||||
|
char_count: usize,
|
||||||
|
index: usize,
|
||||||
|
allow_partial: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CharPairs<'a> {
|
||||||
|
fn new(string: &'a str, allow_partial: bool) -> Self {
|
||||||
|
let mut char_pairs = Self {
|
||||||
|
string,
|
||||||
|
chars: string.char_indices(),
|
||||||
|
char_count: 0,
|
||||||
|
index: 0,
|
||||||
|
allow_partial,
|
||||||
|
};
|
||||||
|
char_pairs.next_char();
|
||||||
|
char_pairs
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_char(&mut self) -> bool {
|
||||||
|
if self.index == self.string.len() {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
match self.chars.next() {
|
||||||
|
Some((i, _)) => {
|
||||||
|
self.char_count += 1;
|
||||||
|
self.index = i;
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
self.index = self.string.len();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for CharPairs<'a> {
|
||||||
|
type Item = Result<&'a str>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let start_index = self.index;
|
||||||
|
if !self.next_char() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.next_char() || self.allow_partial {
|
||||||
|
Some(Ok(&self.string[start_index..self.index]))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Some(Err(Error::error(&format!("invalid string: got {} characters, expected even number", self.char_count))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn parse_byte(maybe_str_byte: Result<&str>) -> Result<u8> {
|
||||||
|
match maybe_str_byte {
|
||||||
|
Ok(str_byte) =>
|
||||||
|
u8::from_str_radix(str_byte, 16)
|
||||||
|
.map_err(|_| Error::error("invalid character")),
|
||||||
|
Err(err) => Err(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::str::{FromStr};
|
||||||
|
|
||||||
|
use crate::error::*;
|
||||||
|
use super::ObjectId;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn object_id_display() {
|
||||||
|
let obj = ObjectId::new(&[
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_eq!(format!("{}", obj), "0001020304050607");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn object_id_debug() {
|
||||||
|
let obj = ObjectId::new(&[
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_eq!(format!("{:?}", obj), "ObjectId::new(\"0001020304050607\")");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn str_to_object_id() {
|
||||||
|
let obj = ObjectId::from_str("0001020304050607")
|
||||||
|
.expect("object id parsing failed");
|
||||||
|
let obj_ref = ObjectId::new(&[
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_eq!(obj, obj_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn str_to_object_id_invalid_size() {
|
||||||
|
let result = ObjectId::from_str("000102030405060");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
result.expect_err("object id parsing should have failed"),
|
||||||
|
Error::error("invalid string: got 15 characters, expected even number"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn str_to_object_id_invalid_character() {
|
||||||
|
let result = ObjectId::from_str("00010203 4050607");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
result.expect_err("object id parsing should have failed"),
|
||||||
|
Error::error("invalid character"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
61
cas-core/src/object_metadata.rs
Normal file
61
cas-core/src/object_metadata.rs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
// This file is part of bsv.
|
||||||
|
//
|
||||||
|
// bsv is free software: you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Affero General Public License as published by the Free
|
||||||
|
// Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
// any later version.
|
||||||
|
//
|
||||||
|
// cdb is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||||
|
// more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the Affero GNU General Public License
|
||||||
|
// along with cdb. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
use super::object_type::{ObjectType};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone, Eq, PartialEq)]
|
||||||
|
pub struct ObjectMetadata {
|
||||||
|
otype: ObjectType,
|
||||||
|
size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectMetadata {
|
||||||
|
pub fn new(otype: ObjectType, size: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
otype,
|
||||||
|
size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn otype(&self) -> &ObjectType {
|
||||||
|
&self.otype
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(&self) -> usize {
|
||||||
|
self.size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for ObjectMetadata {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||||
|
f.write_fmt(format_args!("<meta {} {}>", self.otype().id().escape_ascii().to_string(), self.size))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn object_metadata_debug() {
|
||||||
|
let meta = ObjectMetadata::new(ObjectType::new(b"tree").unwrap(), 1234);
|
||||||
|
|
||||||
|
assert_eq!(meta, meta);
|
||||||
|
assert_eq!(format!("{:?}", meta), "<meta tree 1234>");
|
||||||
|
}
|
||||||
|
}
|
||||||
63
cas-core/src/object_type.rs
Normal file
63
cas-core/src/object_type.rs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
// This file is part of bsv.
|
||||||
|
//
|
||||||
|
// bsv is free software: you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Affero General Public License as published by the Free
|
||||||
|
// Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
// any later version.
|
||||||
|
//
|
||||||
|
// cdb is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||||
|
// more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the Affero GNU General Public License
|
||||||
|
// along with cdb. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use super::error::*;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
|
pub struct ObjectType {
|
||||||
|
id: [u8; 4],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectType {
|
||||||
|
pub fn new(id: &[u8]) -> Result<Self> {
|
||||||
|
if id.len() != 4 {
|
||||||
|
Err(Error::error("Invalid object type size."))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let mut buf = [0; 4];
|
||||||
|
for (i, b) in id.iter().enumerate() {
|
||||||
|
buf[i] = *b;
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
id: buf,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(&self) -> &[u8; 4] {
|
||||||
|
&self.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for ObjectType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||||
|
f.write_fmt(format_args!("<otype {}>", self.id.escape_ascii().to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn object_type_debug() {
|
||||||
|
let otype = ObjectType::new(b"tree").unwrap();
|
||||||
|
|
||||||
|
assert_eq!(otype, otype);
|
||||||
|
assert_eq!(format!("{:?}", otype), "<otype tree>");
|
||||||
|
}
|
||||||
|
}
|
||||||
252
cas-core/src/pipeline.rs
Normal file
252
cas-core/src/pipeline.rs
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
// This file is part of bsv.
|
||||||
|
//
|
||||||
|
// bsv is free software: you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Affero General Public License as published by the Free
|
||||||
|
// Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
// any later version.
|
||||||
|
//
|
||||||
|
// cdb is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||||
|
// more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the Affero GNU General Public License
|
||||||
|
// along with cdb. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use digest::DynDigest;
|
||||||
|
|
||||||
|
use super::error::*;
|
||||||
|
use super::object_id::ObjectId;
|
||||||
|
|
||||||
|
|
||||||
|
pub trait Reader {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
|
||||||
|
fn finalize(self: Box<Self>) -> Result<ObjectId>;
|
||||||
|
|
||||||
|
fn read_all(&mut self) -> Result<Vec<u8>> {
|
||||||
|
let mut buffer = [0u8; 1 << 13];
|
||||||
|
let mut data = Vec::new();
|
||||||
|
let mut count = usize::MAX;
|
||||||
|
|
||||||
|
while count != 0 {
|
||||||
|
count = self.read(&mut buffer)?;
|
||||||
|
if count != 0 {
|
||||||
|
data.extend_from_slice(&buffer[..count]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Writer {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> Result<()>;
|
||||||
|
fn finalize(self: Box<Self>) -> Result<ObjectId>;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub trait Pipeline {
|
||||||
|
fn new_reader(&self, reader: Box<dyn Reader>) -> Box<dyn Reader>;
|
||||||
|
fn new_writer(&self, writer: Box<dyn Writer>) -> Box<dyn Writer>;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<R: std::io::Read> Reader for R {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||||
|
<Self as std::io::Read>::read(self, buf).or_else(|err|
|
||||||
|
Err(Error::error(format!("Read error: {}", err)))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finalize(self: Box<Self>) -> Result<ObjectId> {
|
||||||
|
Err(Error::error("reader pipline has no digest step"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<W: std::io::Write> Writer for W {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> Result<()> {
|
||||||
|
self.write_all(buf).or_else(|err|
|
||||||
|
Err(Error::error(format!("Write error: {}", err)))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finalize(self: Box<Self>) -> Result<ObjectId> {
|
||||||
|
Err(Error::error("reader pipline has no digest step"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct DigestReader<'a> {
|
||||||
|
reader: Box<dyn Reader + 'a>,
|
||||||
|
digest: Option<Box<dyn DynDigest + 'a>>,
|
||||||
|
oid: Option<ObjectId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DigestReader<'a> {
|
||||||
|
pub fn new(reader: Box<dyn Reader + 'a>, digest: Box<dyn DynDigest + 'a>) -> Self {
|
||||||
|
return DigestReader {
|
||||||
|
reader,
|
||||||
|
digest: Some(digest),
|
||||||
|
oid: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Reader for DigestReader<'a> {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||||
|
let count = if let Some(dig) = &mut self.digest {
|
||||||
|
let count = self.reader.read(buf)?;
|
||||||
|
dig.update(&buf[..count]);
|
||||||
|
count
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
if count == 0 {
|
||||||
|
if let Some(digest) = self.digest.take() {
|
||||||
|
self.oid = Some(ObjectId::new(&digest.finalize()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finalize(self: Box<Self>) -> Result<ObjectId> {
|
||||||
|
self.oid.ok_or_else(|| Error::error("Reader not finalized"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct DigestWriter<'a> {
|
||||||
|
writer: Box<dyn Writer + 'a>,
|
||||||
|
digest: Box<dyn DynDigest + 'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DigestWriter<'a> {
|
||||||
|
pub fn new(writer: Box<dyn Writer + 'a>, digest: Box<dyn DynDigest + 'a>) -> Self {
|
||||||
|
return DigestWriter {
|
||||||
|
writer,
|
||||||
|
digest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Writer for DigestWriter<'a> {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> Result<()> {
|
||||||
|
self.digest.update(buf);
|
||||||
|
self.writer.write(buf)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finalize(self: Box<Self>) -> Result<ObjectId> {
|
||||||
|
Ok(ObjectId::new(&self.digest.finalize()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct DefaultPipeline {
|
||||||
|
digest: Box<dyn DynDigest>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DefaultPipeline {
|
||||||
|
pub fn new(digest: Box<dyn DynDigest>) -> Self {
|
||||||
|
Self {
|
||||||
|
digest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pipeline for DefaultPipeline {
|
||||||
|
fn new_reader(&self, reader: Box<dyn Reader>) -> Box<dyn Reader> {
|
||||||
|
Box::new(DigestReader::new(reader, self.digest.box_clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_writer(&self, writer: Box<dyn Writer>) -> Box<dyn Writer> {
|
||||||
|
Box::new(DigestWriter::new(writer, self.digest.box_clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
extern crate sha2;
|
||||||
|
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use sha2::Sha256;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn std_read_as_reader() {
|
||||||
|
let data = b"Hello World!".to_vec();
|
||||||
|
let mut reader: Box<dyn Reader> = Box::new(data.as_slice());
|
||||||
|
|
||||||
|
let data2 = reader.read_all().expect("read failed");
|
||||||
|
let maybe_oid = reader.finalize();
|
||||||
|
|
||||||
|
assert_eq!(data2, data);
|
||||||
|
assert!(maybe_oid.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn std_write_as_writer() {
|
||||||
|
let data = b"Hello World!".to_vec();
|
||||||
|
let mut buffer = [0u8; 32];
|
||||||
|
|
||||||
|
let maybe_oid = {
|
||||||
|
let mut writer: Box<dyn Writer> = Box::new(&mut buffer[..]);
|
||||||
|
writer.write(&data).expect("write failed");
|
||||||
|
writer.finalize()
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(buffer[..data.len()], data);
|
||||||
|
// Check that the rest of the buffer is unchanged
|
||||||
|
assert!(buffer[data.len()..].iter().all(|&v| v == 0));
|
||||||
|
assert!(maybe_oid.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn digest_reader() {
|
||||||
|
let data = b"Hello World!".to_vec();
|
||||||
|
let oid = ObjectId::from_str(
|
||||||
|
"7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069"
|
||||||
|
).expect("invalid object id");
|
||||||
|
|
||||||
|
let mut reader = Box::new(DigestReader::new(
|
||||||
|
Box::new(data.as_slice()),
|
||||||
|
Box::new(Sha256::default()),
|
||||||
|
));
|
||||||
|
|
||||||
|
let data2 = reader.read_all().expect("read failed");
|
||||||
|
let oid2 = reader.finalize().expect("finalize failed");
|
||||||
|
|
||||||
|
assert_eq!(data2, data);
|
||||||
|
assert_eq!(oid2, oid);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn digest_writer() {
|
||||||
|
let data = b"Hello World!".to_vec();
|
||||||
|
let oid = ObjectId::from_str(
|
||||||
|
"7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069"
|
||||||
|
).expect("invalid object id");
|
||||||
|
|
||||||
|
let mut buffer = [0u8; 32];
|
||||||
|
let oid2 = {
|
||||||
|
let mut writer = Box::new(DigestWriter::new(
|
||||||
|
Box::new(&mut buffer[..]),
|
||||||
|
Box::new(Sha256::default()),
|
||||||
|
));
|
||||||
|
writer.write(&data).expect("write failed");
|
||||||
|
writer.finalize().expect("finalize failed")
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(buffer[..data.len()], data);
|
||||||
|
// Check that the rest of the buffer is unchanged
|
||||||
|
assert!(buffer[data.len()..].iter().all(|&v| v == 0));
|
||||||
|
assert_eq!(oid2, oid);
|
||||||
|
}
|
||||||
|
}
|
||||||
15
cas-simple/Cargo.toml
Normal file
15
cas-simple/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "cas-simple"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
digest = { version = "0.9.0", features = ["alloc"] }
|
||||||
|
sha2 = "0.9.5"
|
||||||
|
toml = "0.5.8"
|
||||||
|
cas-core = { path = "../cas-core" }
|
||||||
|
tempfile = "3.2.0"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
245
cas-simple/src/lib.rs
Normal file
245
cas-simple/src/lib.rs
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
// This file is part of bsv.
|
||||||
|
//
|
||||||
|
// bsv is free software: you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Affero General Public License as published by the Free
|
||||||
|
// Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
// any later version.
|
||||||
|
//
|
||||||
|
// cdb is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||||
|
// more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the Affero GNU General Public License
|
||||||
|
// along with cdb. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! # cas-simple
|
||||||
|
//!
|
||||||
|
//! `cas-simple` implements a simple content addressable storage using
|
||||||
|
//! cas-core.
|
||||||
|
|
||||||
|
|
||||||
|
extern crate digest;
|
||||||
|
extern crate sha2;
|
||||||
|
extern crate toml;
|
||||||
|
extern crate cas_core;
|
||||||
|
|
||||||
|
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use digest::DynDigest;
|
||||||
|
use toml::Value;
|
||||||
|
use cas_core::{Cas, Error, hex, ObjectId, ObjectMetadata, ObjectType, Result};
|
||||||
|
|
||||||
|
|
||||||
|
fn new_digest(id: &str) -> Result<Box<dyn DynDigest>> {
|
||||||
|
match id {
|
||||||
|
"sha256" => { Ok(Box::new(sha2::Sha256::default())) },
|
||||||
|
_ => { Err(Error::error(format!("unknown digest '{}'", id))) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct SimpleCas {
|
||||||
|
db_path: PathBuf,
|
||||||
|
digest: Box<dyn DynDigest>,
|
||||||
|
config: Value,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl SimpleCas {
|
||||||
|
pub fn create(db_path: PathBuf, config: Value) -> Result<Self> {
|
||||||
|
let digest_id = config["cas"]["digest"].as_str()
|
||||||
|
.ok_or_else(|| Error::error(
|
||||||
|
"mandatory cas.digest value is invalid or missing from config"
|
||||||
|
))?;
|
||||||
|
let digest = new_digest(digest_id)?;
|
||||||
|
|
||||||
|
if db_path.exists() {
|
||||||
|
return Err(Error::error(format!(
|
||||||
|
"failed to create SimpleCas: target directory already exists ({})",
|
||||||
|
db_path.to_string_lossy(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
for path in [
|
||||||
|
&db_path,
|
||||||
|
&obj_dir(&db_path),
|
||||||
|
&ref_dir(&db_path),
|
||||||
|
&tmp_dir(&db_path),
|
||||||
|
] {
|
||||||
|
std::fs::create_dir(path).or_else(|e|
|
||||||
|
Err(Error::error(format!(
|
||||||
|
"failed to create directory ({}): {}",
|
||||||
|
path.to_string_lossy(), e,
|
||||||
|
)))
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_config(&config, &db_path)?;
|
||||||
|
|
||||||
|
Ok(SimpleCas {
|
||||||
|
db_path,
|
||||||
|
digest,
|
||||||
|
config,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open(db_path: PathBuf) -> Result<Self> {
|
||||||
|
let config = read_config(&db_path)?;
|
||||||
|
|
||||||
|
let digest_id = config["cas"]["digest"].as_str()
|
||||||
|
.ok_or_else(|| Error::error(
|
||||||
|
"mandatory cas.digest value is invalid or missing from config"
|
||||||
|
))?;
|
||||||
|
let digest = new_digest(digest_id)?;
|
||||||
|
|
||||||
|
Ok(SimpleCas {
|
||||||
|
db_path,
|
||||||
|
digest,
|
||||||
|
config,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save_config(&self) -> Result<()> {
|
||||||
|
write_config(&self.config, &self.db_path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Cas for SimpleCas {
|
||||||
|
fn object_id_from_string(&self, hex: &str) -> Result<ObjectId> {
|
||||||
|
if hex.len() == self.digest.output_size() * 2 {
|
||||||
|
ObjectId::from_str(hex)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Err(Error::error(format!(
|
||||||
|
"invalid object id size: got {}, expected {}",
|
||||||
|
hex.len(), self.digest.output_size() * 2,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn object_id_from_partial(&self, _hex: &str) -> Result<ObjectId> {
|
||||||
|
Err(Error::error("Not implemented"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_object_id(&self, oid: &ObjectId) -> Result<bool> {
|
||||||
|
let opath = obj_path(&self.db_path, oid);
|
||||||
|
Ok(opath.is_file())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_object(&self, _oid: &ObjectId) -> Result<(ObjectMetadata, &dyn std::io::Read)> {
|
||||||
|
Err(Error::error("Not implemented"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_object(&mut self, _otype: ObjectType, _data: &mut dyn std::io::Read) -> Result<ObjectId> {
|
||||||
|
Err(Error::error("Not implemented"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_object(&mut self, _oid: &ObjectId) -> Result<()> {
|
||||||
|
Err(Error::error("Not implemented"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_ref(&self, _key: &str) -> Result<ObjectId> {
|
||||||
|
Err(Error::error("Not implemented"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_ref(&mut self, _key: &str, _value: &ObjectId) -> Result<()> {
|
||||||
|
Err(Error::error("Not implemented"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_ref(&mut self, _key: &str) -> Result<()> {
|
||||||
|
Err(Error::error("Not implemented"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn obj_dir(cas_path: &Path) -> PathBuf {
|
||||||
|
cas_path.join("obj")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn obj_path(cas_path: &Path, oid: &ObjectId) -> PathBuf {
|
||||||
|
let mut path = cas_path.to_path_buf();
|
||||||
|
path.push(hex(&oid.id()[0..1]));
|
||||||
|
path.push(hex(&oid.id()[1..2]));
|
||||||
|
path.push(hex(&oid.id()[2..]));
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ref_dir(cas_path: &Path) -> PathBuf {
|
||||||
|
cas_path.join("ref")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tmp_dir(cas_path: &Path) -> PathBuf {
|
||||||
|
cas_path.join("tmp")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn config_path(cas_path: &Path) -> PathBuf {
|
||||||
|
cas_path.join("config")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_config(db_path: &Path) -> Result<Value> {
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
let mut file = std::fs::File::open(config_path(db_path)).or_else(|err|
|
||||||
|
Err(Error::error(format!("invalid repository: no config file: {}", err)))
|
||||||
|
)?;
|
||||||
|
let mut config_str = String::new();
|
||||||
|
file.read_to_string(&mut config_str).or_else(|err|
|
||||||
|
Err(Error::error(format!("cannot read config file: {}", err)))
|
||||||
|
)?;
|
||||||
|
let config = toml::from_str(&config_str).or_else(|err|
|
||||||
|
Err(Error::error(format!("error while reading config file: {}", err)))
|
||||||
|
)?;
|
||||||
|
Ok(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_config(config: &Value, db_path: &Path) -> Result<()> {
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
let config_str = toml::to_string_pretty(config).or_else(|err|
|
||||||
|
Err(Error::error(format!("failed to serialize config: {}", err)))
|
||||||
|
)?;
|
||||||
|
let mut file = tempfile::NamedTempFile::new_in(tmp_dir(db_path)).or_else(|err|
|
||||||
|
Err(Error::error(format!("cannot create temp config file: {}", err)))
|
||||||
|
)?;
|
||||||
|
file.write_all(config_str.as_bytes()).or_else(|err|
|
||||||
|
Err(Error::error(format!("failed to write to temp config: {}", err)))
|
||||||
|
)?;
|
||||||
|
file.persist(config_path(db_path)).or_else(|err|
|
||||||
|
Err(Error::error(format!("failed to (over)write config: {}", err)))
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
extern crate tempfile;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn create_simple_cas() {
|
||||||
|
let dir = tempfile::tempdir()
|
||||||
|
.expect("failed to create temp test dir");
|
||||||
|
let cas_path = {
|
||||||
|
let mut cas_path = dir.path().to_path_buf();
|
||||||
|
cas_path.push(".bsv");
|
||||||
|
cas_path
|
||||||
|
};
|
||||||
|
let config = toml::toml!(
|
||||||
|
[cas]
|
||||||
|
digest = "sha256"
|
||||||
|
);
|
||||||
|
|
||||||
|
let cas = SimpleCas::create(cas_path, config)
|
||||||
|
.expect("failed to create SimpleCas object");
|
||||||
|
let oid = cas.object_id_from_string("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
|
||||||
|
.expect("failed to create object id");
|
||||||
|
|
||||||
|
assert!(!cas.has_object_id(&oid).expect("has_object_id failed"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ edition = "2018"
|
|||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
error-chain = "0.12.2"
|
thiserror = "1.0.25"
|
||||||
serde = { version = "1.0.106", features = ["derive"] }
|
serde = { version = "1.0.106", features = ["derive"] }
|
||||||
toml = "0.5.6"
|
toml = "0.5.6"
|
||||||
uuid = { version = "0.8.1", features = ["serde", "v4"] }
|
uuid = { version = "0.8.1", features = ["serde", "v4"] }
|
||||||
|
|||||||
@@ -17,50 +17,99 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
|
||||||
error_chain! {
|
pub type Result<T> = std::result::Result<T, Box<Error>>;
|
||||||
foreign_links {
|
|
||||||
Io(::std::io::Error);
|
|
||||||
}
|
|
||||||
|
|
||||||
errors {
|
#[non_exhaustive]
|
||||||
NoRepositorySpecifiedError {
|
#[derive(Debug, thiserror::Error)]
|
||||||
description("No repository specifed")
|
pub enum Error {
|
||||||
display("No repository specifed. Use -r or set BSV_REPOSITORY environment variable.")
|
#[error("failed to create repository: {message}")]
|
||||||
}
|
RepositoryCreationFailed {
|
||||||
|
message: String,
|
||||||
|
source: Option<Box<dyn std::error::Error>>,
|
||||||
|
},
|
||||||
|
|
||||||
NonEmptyDirectory(dir: PathBuf) {
|
#[error("invalid object id: {message}")]
|
||||||
description("Non-empty directory")
|
InvalidObjectId {
|
||||||
display("Target directory {:?} must be empty.", dir)
|
message: String,
|
||||||
}
|
source: Option<Box<dyn std::error::Error>>,
|
||||||
|
},
|
||||||
|
|
||||||
InvalidObjectIdSize {
|
#[error("invalid size (got: {size}, expected: {expected})")]
|
||||||
description("Object id has an invalid size")
|
InvalidSize {
|
||||||
display("Object id has an invalid size.")
|
size: usize,
|
||||||
}
|
expected: usize,
|
||||||
|
},
|
||||||
|
|
||||||
InvalidObjectIdCharacter {
|
#[error("non-empty directory ({dir})")]
|
||||||
description("Object id contains invalid character")
|
NonEmptyDirectory {
|
||||||
display("Object id contains invalid character.")
|
dir: PathBuf
|
||||||
}
|
},
|
||||||
|
|
||||||
InvalidObjectType(otype: [u8; 4]) {
|
#[error("invalid character(s) ({characters})")]
|
||||||
description("Object has an invalid object type")
|
InvalidObjectIdCharacter {
|
||||||
display("Object has an invalid object type {:?}.", otype)
|
characters: String,
|
||||||
}
|
},
|
||||||
|
|
||||||
MismatchingObjectSize(actual: u64, expected: u64) {
|
#[error("invalid object type ({otype:?})")]
|
||||||
description("Mismatching object size")
|
InvalidObjectType {
|
||||||
display("Mismatching object size: expected {}, got {}.", actual, expected)
|
otype: [u8; 4],
|
||||||
}
|
},
|
||||||
|
|
||||||
UnsupportedFileType {
|
#[error("invalid object size (expected {expected}, got {size})")]
|
||||||
description("Unsupported file type")
|
InvalidObjectSize {
|
||||||
display("Unsupported file type.")
|
size: u64,
|
||||||
}
|
expected: u64,
|
||||||
|
},
|
||||||
|
|
||||||
InvalidPath(path: PathBuf) {
|
#[error("unsupported file type")]
|
||||||
description("Invalid path")
|
UnsupportedFileType,
|
||||||
display("Invalid path: {:?}.", path)
|
|
||||||
}
|
#[error("invalid path ({path})")]
|
||||||
|
InvalidPath { path: PathBuf },
|
||||||
|
|
||||||
|
#[error("io error{}", format_optional_path(path))]
|
||||||
|
IoError {
|
||||||
|
source: std::io::Error,
|
||||||
|
path: Option<PathBuf>,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("{0}")]
|
||||||
|
Other(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn repository_creation_failed<M: Into<String>>(message: M) -> Box<Error> {
|
||||||
|
Box::new(Error::RepositoryCreationFailed {
|
||||||
|
message: message.into(),
|
||||||
|
source: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn repository_creation_failed_from<M: Into<String>>(source: Box<dyn std::error::Error>, message: M) -> Box<Error> {
|
||||||
|
Box::new(Error::RepositoryCreationFailed {
|
||||||
|
message: message.into(),
|
||||||
|
source: Some(source),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn invalid_object_id<M: Into<String>>(message: M) -> Box<Error> {
|
||||||
|
Box::new(Error::InvalidObjectId {
|
||||||
|
message: message.into(),
|
||||||
|
source: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn invalid_object_id_from<M: Into<String>>(source: Box<dyn std::error::Error>, message: M) -> Box<Error> {
|
||||||
|
Box::new(Error::InvalidObjectId {
|
||||||
|
message: message.into(),
|
||||||
|
source: Some(source),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn format_optional_path(maybe_path: &Option<PathBuf>) -> String {
|
||||||
|
match maybe_path {
|
||||||
|
Some(path) => { format!(" ({:?})", path) },
|
||||||
|
None => { String::new() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,18 +15,18 @@
|
|||||||
|
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod config;
|
// pub mod config;
|
||||||
pub mod object_id;
|
// pub mod object_id;
|
||||||
pub mod object;
|
// pub mod object;
|
||||||
pub mod repository;
|
// pub mod repository;
|
||||||
|
|
||||||
|
|
||||||
pub const NAME: &str = env!("CARGO_PKG_NAME");
|
pub const NAME: &str = env!("CARGO_PKG_NAME");
|
||||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
|
|
||||||
pub use error::{Error, Result, ErrorKind};
|
pub use error::{Error, Result};
|
||||||
pub use config::{Config, RepositoryConfig};
|
// pub use config::{Config, RepositoryConfig};
|
||||||
pub use object_id::ObjectId;
|
// pub use object_id::ObjectId;
|
||||||
pub use object::{ObjectType, OTYPE_BLOB, OTYPE_TREE};
|
// pub use object::{ObjectType, OTYPE_BLOB, OTYPE_TREE};
|
||||||
pub use repository::Repository;
|
// pub use repository::Repository;
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ pub fn object_type_from_metadata(md: &Metadata) -> Result<ObjectType> {
|
|||||||
Ok(*OTYPE_TREE)
|
Ok(*OTYPE_TREE)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Err(ErrorKind::UnsupportedFileType.into())
|
Err(Error::UnsupportedFileType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +137,12 @@ impl TreeItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_file_path_and_id(file_path: &Path, oid: &ObjectId) -> Result<TreeItem> {
|
pub fn from_file_path_and_id(file_path: &Path, oid: &ObjectId) -> Result<TreeItem> {
|
||||||
let md = file_path.symlink_metadata()?;
|
let md = file_path.symlink_metadata()
|
||||||
|
.map_err(|err| Error::IoError {
|
||||||
|
source: err,
|
||||||
|
path: Some(file_path.to_path_buf())
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(TreeItem {
|
Ok(TreeItem {
|
||||||
otype: object_type_from_metadata(&md)?,
|
otype: object_type_from_metadata(&md)?,
|
||||||
oid: oid.clone(),
|
oid: oid.clone(),
|
||||||
@@ -145,7 +150,7 @@ impl TreeItem {
|
|||||||
permissions: Permissions::from_unix_mode(md.mode()),
|
permissions: Permissions::from_unix_mode(md.mode()),
|
||||||
name: file_path.file_name()
|
name: file_path.file_name()
|
||||||
.and_then(|n| n.to_str())
|
.and_then(|n| n.to_str())
|
||||||
.ok_or_else(|| ErrorKind::InvalidPath(file_path.into()))?
|
.ok_or_else(|| Error::InvalidPath { path: file_path.into() })?
|
||||||
.into(),
|
.into(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ impl FromStr for ObjectId {
|
|||||||
|
|
||||||
fn from_str(id_str: &str) -> Result<ObjectId> {
|
fn from_str(id_str: &str) -> Result<ObjectId> {
|
||||||
if id_str.len() % 2 != 0 {
|
if id_str.len() % 2 != 0 {
|
||||||
return Err(ErrorKind::InvalidObjectIdSize.into());
|
return Err(Error::InvalidObjectIdSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
let byte_count = id_str.len() / 2;
|
let byte_count = id_str.len() / 2;
|
||||||
@@ -53,9 +53,9 @@ impl FromStr for ObjectId {
|
|||||||
for byte_index in 0..byte_count {
|
for byte_index in 0..byte_count {
|
||||||
let str_index = byte_index * 2;
|
let str_index = byte_index * 2;
|
||||||
let byte_str = id_str.get(str_index..(str_index + 2))
|
let byte_str = id_str.get(str_index..(str_index + 2))
|
||||||
.ok_or(ErrorKind::InvalidObjectIdCharacter)?;
|
.ok_or(Error::InvalidObjectIdCharacter)?;
|
||||||
id.push(u8::from_str_radix(byte_str, 16)
|
id.push(u8::from_str_radix(byte_str, 16)
|
||||||
.or(Err(ErrorKind::InvalidObjectIdCharacter))?);
|
.or(Err(Error::InvalidObjectIdCharacter))?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ObjectId {
|
Ok(ObjectId {
|
||||||
|
|||||||
@@ -39,14 +39,14 @@ pub struct Repository {
|
|||||||
impl Repository {
|
impl Repository {
|
||||||
pub fn create(path: &Path, device_name: &str) -> Result<Repository> {
|
pub fn create(path: &Path, device_name: &str) -> Result<Repository> {
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
bail!(ErrorKind::NonEmptyDirectory(path.into()))
|
return Err(Error::NonEmptyDirectory { dir: path.to_path_buf() });
|
||||||
}
|
}
|
||||||
|
|
||||||
if device_name.is_empty() {
|
if device_name.is_empty() {
|
||||||
bail!("Device name must not be empty.")
|
return Err(Error::RepositoryCreationFailed("Device name must not be empty.".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
create_dir(&path).chain_err(|| "Failed to create repository.")?;
|
create_dir(&path).map_err(|err| Error::IoError("Failed to create repository.")?;
|
||||||
|
|
||||||
let config = Rc::new(
|
let config = Rc::new(
|
||||||
Config {
|
Config {
|
||||||
|
|||||||
@@ -14,20 +14,20 @@
|
|||||||
// along with cdb. If not, see <https://www.gnu.org/licenses/>.
|
// along with cdb. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
#[macro_use]
|
// #[macro_use]
|
||||||
extern crate error_chain;
|
extern crate thiserror;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
|
|
||||||
|
|
||||||
pub mod core;
|
pub mod core;
|
||||||
pub mod simple_db;
|
// pub mod simple_db;
|
||||||
|
|
||||||
|
|
||||||
pub use crate::core::{
|
// pub use crate::core::{
|
||||||
Error, Result, ErrorKind,
|
// Error, Result,
|
||||||
Config, RepositoryConfig,
|
// Config, RepositoryConfig,
|
||||||
ObjectId, ObjectType,
|
// ObjectId, ObjectType,
|
||||||
Repository,
|
// Repository,
|
||||||
|
|
||||||
OTYPE_BLOB, OTYPE_TREE,
|
// OTYPE_BLOB, OTYPE_TREE,
|
||||||
};
|
// };
|
||||||
|
|||||||
Reference in New Issue
Block a user