diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..051d09d --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +eval "$(lorri direnv)" diff --git a/.gitignore b/.gitignore index 8ffd9f4..cf7df87 100644 --- a/.gitignore +++ b/.gitignore @@ -4,10 +4,6 @@ debug/ target/ -# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries -# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html -Cargo.lock - # These are backup files generated by rustfmt **/*.rs.bk diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..4c2123e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,161 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66c99696f6c9dd7f35d486b9d04d7e6e202aa3e8c40d553f2fdf5e7e0c6a71ef" +dependencies = [ + "libc", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "libc" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5" + +[[package]] +name = "logtail" +version = "0.1.0" +dependencies = [ + "hex", + "rand", + "sha2", +] + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core", +] + +[[package]] +name = "sha2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] + +[[package]] +name = "typenum" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..c66a4d7 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["crates/*"] diff --git a/crates/logtail/Cargo.toml b/crates/logtail/Cargo.toml new file mode 100644 index 0000000..193688b --- /dev/null +++ b/crates/logtail/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "logtail" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +hex = "0.4" +rand = "0" +sha2 = "0.9" diff --git a/crates/logtail/src/lib.rs b/crates/logtail/src/lib.rs new file mode 100644 index 0000000..3e8e232 --- /dev/null +++ b/crates/logtail/src/lib.rs @@ -0,0 +1,115 @@ +use sha2::{Digest, Sha256}; +use std::fmt; + +/// This represents an instance that write logs. +/// Private IDs are only shared with the server when writing logs. +#[derive(Clone, PartialEq, Eq)] +pub struct PrivateID([u8; SHA256_SIZE]); + +impl PrivateID { + /// Safely generate a new PrivateId for use in Config objects. + /// You should persist this across runs of an instance of your app, so that + /// it can append to the same log file on each run. + pub fn new() -> Self { + let mut id: [u8; 32] = rand::random(); + id[0] &= 248; + id[31] = (id[31] & 127) | 64; + Self(id) + } + + /// Parse a PrivateID from its base 16 representation. + pub fn from_hex(data: String) -> Result { + let mut id: [u8; 32] = [0; 32]; + hex::decode_to_slice(data, &mut id)?; + Ok(Self(id)) + } + + /// Show a private ID as its base 16 representation. + pub fn as_hex(&self) -> String { + hex::encode(self.0) + } + + pub fn as_public(&self) -> PublicID { + let mut hasher = Sha256::new(); + hasher.update(&self.0); + PublicID(hasher.finalize().into()) + } +} + +impl fmt::Display for PrivateID { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}", self.as_hex()) + } +} + +impl fmt::Debug for PrivateID { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "PrivateID({})", self.as_hex()) + } +} + +const SHA256_SIZE: usize = 32; + +#[derive(Clone, PartialEq, Eq)] +pub struct PublicID([u8; SHA256_SIZE]); + +impl PublicID { + /// Parse a PublicID from its base 16 representation. + pub fn from_hex(data: String) -> Result { + let mut id: [u8; 32] = [0; 32]; + hex::decode_to_slice(data, &mut id)?; + Ok(Self(id)) + } + + /// Show a public ID as its base 16 representation. + pub fn as_hex(&self) -> String { + hex::encode(self.0) + } +} + +impl fmt::Display for PublicID { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}", self.as_hex()) + } +} + +impl fmt::Debug for PublicID { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "PublicID({})", self.as_hex()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn id() { + let id = PrivateID::new(); + assert_ne!(id.as_hex(), "".to_string()); + } + + #[test] + fn encode_decode() { + let id = PrivateID::new(); + let as_hex = id.as_hex(); + let other_id = PrivateID::from_hex(as_hex).unwrap(); + assert_eq!(id, other_id); + } + + #[test] + fn to_public() { + let id = PrivateID::from_hex( + "6451f1641112c13e80daa7237d62391b028e638fef9cf91fd7ea347a5290a643".to_string(), + ) + .unwrap(); + let pub_id = id.as_public(); + assert_eq!( + pub_id, + PublicID::from_hex( + "db3a35df70555653e5fcff5bbc06e1573ad67e00744b56691c15cde72d17c449".to_string() + ) + .unwrap() + ); + } +} diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..54bca02 --- /dev/null +++ b/shell.nix @@ -0,0 +1,10 @@ +{ pkgs ? import {} }: + +pkgs.mkShell { + buildInputs = with pkgs; [ + rustc cargo rust-analyzer rustfmt + + # keep this line if you use bash + bashInteractive + ]; +}