diff --git a/.gitignore b/.gitignore index 3857328..f414b3b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ .conflict-side-*/ .DS_Store .sqlx/ +.stack-work/ .tmp/ .worktrees/ result diff --git a/Cargo.lock b/Cargo.lock index 1546aaf..cfce901 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -504,7 +504,7 @@ dependencies = [ [[package]] name = "event-sorcery" -version = "0.1.2" +version = "0.4.0" dependencies = [ "apalis-core", "async-trait", @@ -1520,7 +1520,7 @@ dependencies = [ [[package]] name = "sqlite-es" -version = "0.1.2" +version = "0.4.0" dependencies = [ "chrono", "cqrs-es", diff --git a/Cargo.toml b/Cargo.toml index 35f56f0..c5beb15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ resolver = "2" [workspace.package] edition = "2024" -version = "0.1.2" +version = "0.4.0" license = "MIT" homepage = "https://github.com/ST0x-Technology/event-sorcery" diff --git a/bindings/haskell/LICENSE b/bindings/haskell/LICENSE new file mode 100644 index 0000000..b888b46 --- /dev/null +++ b/bindings/haskell/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 ST0x Technology + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/bindings/haskell/event-sorcery.cabal b/bindings/haskell/event-sorcery.cabal new file mode 100644 index 0000000..200d045 --- /dev/null +++ b/bindings/haskell/event-sorcery.cabal @@ -0,0 +1,45 @@ +cabal-version: 3.0 +name: event-sorcery +version: 0.4.0.0 +synopsis: Type-driven event sourcing over the event-sorcery engine +description: + An idiomatic Haskell binding over the shared event-sorcery Rust engine. + +homepage: https://github.com/dataclique/event-sorcery +bug-reports: https://github.com/dataclique/event-sorcery/issues +license: MIT +license-file: LICENSE +build-type: Simple +category: Database + +common strict + default-language: GHC2024 + default-extensions: + BlockArguments + CApiFFI + DeriveAnyClass + DerivingVia + DuplicateRecordFields + GADTs + LinearTypes + MultiParamTypeClasses + MultilineStrings + NoFieldSelectors + NoImplicitPrelude + OverloadedRecordDot + OverloadedStrings + RankNTypes + StandaloneDeriving + TypeApplications + TypeFamilies + TypeFamilyDependencies + UndecidableInstances + + ghc-options: + -Wall -Wcompat -Werror -Widentities -Wincomplete-record-updates + -Wincomplete-uni-patterns -Wmissing-export-lists + -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints + +library + import: strict + build-depends: base >=4.18 && <5 diff --git a/cabal.project b/cabal.project new file mode 100644 index 0000000..ea6dfed --- /dev/null +++ b/cabal.project @@ -0,0 +1,3 @@ +packages: bindings/haskell + +tests: True diff --git a/crates/sqlite-es/Cargo.toml b/crates/sqlite-es/Cargo.toml index 16f3500..f33eecd 100644 --- a/crates/sqlite-es/Cargo.toml +++ b/crates/sqlite-es/Cargo.toml @@ -1,7 +1,9 @@ [package] name = "sqlite-es" -version = "0.1.2" -edition = "2024" +version.workspace = true +edition.workspace = true +license.workspace = true +homepage.workspace = true [dependencies] cqrs-es.workspace = true diff --git a/flake.lock b/flake.lock index e84368d..dfa147e 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,24 @@ { "nodes": { + "but-nix": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1783635003, + "narHash": "sha256-jK3fvDpOF/w9ojqGu3XmiuvR+0lHf5pEOZQIMC6Z1Lo=", + "owner": "dataclique", + "repo": "but.nix", + "rev": "3aa2ef2086cbddff42f8ec877edf1b05a30e1bdd", + "type": "github" + }, + "original": { + "owner": "dataclique", + "repo": "but.nix", + "type": "github" + } + }, "flake-compat": { "flake": false, "locked": { @@ -16,6 +35,22 @@ "type": "github" } }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1767039857, + "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", + "owner": "NixOS", + "repo": "flake-compat", + "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "flake-compat", + "type": "github" + } + }, "flake-utils": { "inputs": { "systems": "systems" @@ -53,6 +88,24 @@ } }, "flake-utils_3": { + "inputs": { + "systems": "systems_3" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_4": { "locked": { "lastModified": 1644229661, "narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=", @@ -67,9 +120,9 @@ "type": "github" } }, - "flake-utils_4": { + "flake-utils_5": { "inputs": { - "systems": "systems_3" + "systems": "systems_4" }, "locked": { "lastModified": 1731533236, @@ -87,8 +140,8 @@ }, "foundry": { "inputs": { - "flake-utils": "flake-utils_3", - "nixpkgs": "nixpkgs" + "flake-utils": "flake-utils_4", + "nixpkgs": "nixpkgs_2" }, "locked": { "lastModified": 1778486972, @@ -104,11 +157,33 @@ "type": "github" } }, - "git-hooks-nix": { + "git-hooks": { "inputs": { "flake-compat": "flake-compat", + "nixpkgs": [ + "rainix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1783008725, + "narHash": "sha256-jGiy6+sxjNWXSjp25uoJuNfyH9zBK1PEDY0lVoL4ibQ=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "bca82caa46d5ec0f5d422c61fb1e30bc51313cbe", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "git-hooks-nix": { + "inputs": { + "flake-compat": "flake-compat_2", "gitignore": "gitignore", - "nixpkgs": "nixpkgs_2" + "nixpkgs": "nixpkgs_3" }, "locked": { "lastModified": 1778507602, @@ -147,6 +222,22 @@ } }, "nixpkgs": { + "locked": { + "lastModified": 1780511130, + "narHash": "sha256-2v9lT4ya59Lh1FqPeLnz1MoX9y/wz2huqfe9RtQZITk=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "535f3e6942cb1cead3929c604320d3db54b542b9", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-25.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { "locked": { "lastModified": 1666753130, "narHash": "sha256-Wff1dGPFSneXJLI2c0kkdWTgxnQ416KE6X4KnFkgPYQ=", @@ -160,7 +251,7 @@ "type": "indirect" } }, - "nixpkgs_2": { + "nixpkgs_3": { "locked": { "lastModified": 1770073757, "narHash": "sha256-Vy+G+F+3E/Tl+GMNgiHl9Pah2DgShmIUBJXmbiQPHbI=", @@ -176,7 +267,7 @@ "type": "github" } }, - "nixpkgs_3": { + "nixpkgs_4": { "locked": { "lastModified": 1778656924, "narHash": "sha256-lKVrom9wOmpC3i7m+uBoGaBdW0PfH3QbLRG1XmuC6YA=", @@ -191,7 +282,7 @@ "type": "github" } }, - "nixpkgs_4": { + "nixpkgs_5": { "locked": { "lastModified": 1744536153, "narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=", @@ -207,7 +298,7 @@ "type": "github" } }, - "nixpkgs_5": { + "nixpkgs_6": { "locked": { "lastModified": 1777641297, "narHash": "sha256-WNGcmeOZ8Tr9dq6ztCspYbzWFswr2mPebM9LpsfGxPk=", @@ -225,10 +316,10 @@ }, "rainix": { "inputs": { - "flake-utils": "flake-utils_2", + "flake-utils": "flake-utils_3", "foundry": "foundry", "git-hooks-nix": "git-hooks-nix", - "nixpkgs": "nixpkgs_3", + "nixpkgs": "nixpkgs_4", "rust-overlay": "rust-overlay", "solc": "solc" }, @@ -248,13 +339,15 @@ }, "root": { "inputs": { - "flake-utils": "flake-utils", + "but-nix": "but-nix", + "flake-utils": "flake-utils_2", + "git-hooks": "git-hooks", "rainix": "rainix" } }, "rust-overlay": { "inputs": { - "nixpkgs": "nixpkgs_4" + "nixpkgs": "nixpkgs_5" }, "locked": { "lastModified": 1778642276, @@ -272,8 +365,8 @@ }, "solc": { "inputs": { - "flake-utils": "flake-utils_4", - "nixpkgs": "nixpkgs_5", + "flake-utils": "flake-utils_5", + "nixpkgs": "nixpkgs_6", "solc-macos-amd64-list-json": "solc-macos-amd64-list-json" }, "locked": { @@ -346,6 +439,21 @@ "repo": "default", "type": "github" } + }, + "systems_4": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index bfea017..ad483e6 100644 --- a/flake.nix +++ b/flake.nix @@ -1,36 +1,82 @@ { - description = "event-sorcery: a Rust event-sourcing library on top of cqrs-es."; + description = "event-sorcery: one Rust engine with Rust and Haskell bindings"; inputs = { rainix.url = "github:rainprotocol/rainix"; flake-utils.url = "github:numtide/flake-utils"; + but-nix.url = "github:dataclique/but.nix"; + + git-hooks.url = "github:cachix/git-hooks.nix"; + git-hooks.inputs.nixpkgs.follows = "rainix/nixpkgs"; }; outputs = - { flake-utils, rainix, ... }: + { + self, + but-nix, + flake-utils, + git-hooks, + rainix, + ... + }: flake-utils.lib.eachDefaultSystem ( system: let pkgs = rainix.pkgs.${system}; + but = but-nix.packages.${system}.default; + haskellPackages = pkgs.haskell.packages.ghc914; + haskellBinding = haskellPackages.callCabal2nix "event-sorcery" ./bindings/haskell { }; + rustBuildInputs = rainix.rust-build-inputs.${system}; + rustToolchain = rainix.rust-toolchain.${system}; + hooks = import ./git-hooks.nix { + inherit + git-hooks + pkgs + rustToolchain + self + system + ; + }; in { - packages = rainix.packages.${system}; + packages = rainix.packages.${system} // { + inherit but; + haskell = haskellBinding; + }; - devShell = pkgs.mkShell { - inherit (rainix.devShells.${system}.default) shellHook; - inherit (rainix.devShells.${system}.default) nativeBuildInputs; + checks = { + formatting = hooks; + haskell = haskellBinding; + }; + + devShells.default = pkgs.mkShell { + nativeBuildInputs = + rustBuildInputs + ++ hooks.enabledPackages + ++ [ + but + haskellPackages.ghc + pkgs.cabal-install + pkgs.cargo-expand + pkgs.cargo-nextest + pkgs.rust-cbindgen + pkgs.fourmolu + pkgs.haskellPackages.cabal-fmt + pkgs.hlint + pkgs.nixfmt + pkgs.nushell + pkgs.prek + pkgs.sqlite + pkgs.sqlx-cli + pkgs.stack + ]; - buildInputs = - with pkgs; - [ - sqlx-cli - cargo-expand - cargo-nextest - nushell - prek - ] - ++ rainix.devShells.${system}.default.buildInputs; + shellHook = '' + ${hooks.shellHook} + ''; }; + + formatter = pkgs.nixfmt-tree; } ); } diff --git a/fourmolu.yaml b/fourmolu.yaml new file mode 100644 index 0000000..195c82a --- /dev/null +++ b/fourmolu.yaml @@ -0,0 +1,15 @@ +indentation: 2 +column-limit: 80 +function-arrows: leading +comma-style: leading +import-export-style: diff-friendly +indent-wheres: true +record-brace-space: true +newlines-between-decls: 2 +haddock-style: single-line +let-style: auto +in-style: right-align +single-constraint-parens: auto +unicode: never +respectful: false +fixities: [] diff --git a/git-hooks.nix b/git-hooks.nix new file mode 100644 index 0000000..73d8d4a --- /dev/null +++ b/git-hooks.nix @@ -0,0 +1,24 @@ +{ + git-hooks, + pkgs, + rustToolchain, + self, + system, +}: +git-hooks.lib.${system}.run { + src = self; + package = pkgs.prek; + + hooks = { + cabal-fmt.enable = true; + fourmolu.enable = true; + hlint.enable = true; + nixfmt.enable = true; + rustfmt = { + enable = true; + package = rustToolchain; + }; + taplo.enable = true; + trim-trailing-whitespace.enable = true; + }; +} diff --git a/stack.yaml b/stack.yaml new file mode 100644 index 0000000..2a8bfd7 --- /dev/null +++ b/stack.yaml @@ -0,0 +1,18 @@ +snapshot: nightly-2026-07-12 +compiler: ghc-9.14.1 +compiler-check: match-exact + +# GHC and Stack come from the Nix development shell. +system-ghc: true +install-ghc: false +notify-if-nix-on-path: false + +packages: + - bindings/haskell + +extra-deps: [] + +ghc-options: + prettyprinter: -optP-Wno-expansion-to-defined + +rebuild-ghc-options: true diff --git a/stack.yaml.lock b/stack.yaml.lock new file mode 100644 index 0000000..bd46844 --- /dev/null +++ b/stack.yaml.lock @@ -0,0 +1,12 @@ +# This file was autogenerated by Stack. +# You should not edit this file by hand. +# For more information, please see the documentation at: +# https://docs.haskellstack.org/en/stable/topics/lock_files + +packages: [] +snapshots: +- completed: + sha256: cb7d0b9185684418fac8da3353d4054f002a98d065b70d501dd6b93cc3289fef + size: 757765 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2026/7/12.yaml + original: nightly-2026-07-12