From 65e17fc55b82da833653f0bff36c8b664bbcc46c Mon Sep 17 00:00:00 2001 From: Sebastian Hugentobler Date: Wed, 1 May 2024 16:21:07 +0200 Subject: [PATCH] bare bones calibre db reading --- .gitignore | 2 + Cargo.lock | 208 ++++++++++++++++++++++++++++++ Cargo.toml | 5 + calibre-db/Cargo.toml | 8 ++ calibre-db/src/calibre.rs | 94 ++++++++++++++ calibre-db/src/data/author.rs | 38 ++++++ calibre-db/src/data/book.rs | 56 ++++++++ calibre-db/src/data/error.rs | 7 + calibre-db/src/data/pagination.rs | 72 +++++++++++ calibre-db/src/lib.rs | 7 + calibre-db/testdata/metadata.db | Bin 0 -> 413696 bytes flake.lock | 145 +++++++++++++++++++++ flake.nix | 148 +++++++++++++++++++++ 13 files changed, 790 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 calibre-db/Cargo.toml create mode 100644 calibre-db/src/calibre.rs create mode 100644 calibre-db/src/data/author.rs create mode 100644 calibre-db/src/data/book.rs create mode 100644 calibre-db/src/data/error.rs create mode 100644 calibre-db/src/data/pagination.rs create mode 100644 calibre-db/src/lib.rs create mode 100644 calibre-db/testdata/metadata.db create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4075fb2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target +result diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..573c35f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,208 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "calibre-db" +version = "0.1.0" +dependencies = [ + "rusqlite", + "thiserror", +] + +[[package]] +name = "cc" +version = "1.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashlink" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692eaaf7f7607518dd3cef090f1474b61edc5301d8012f09579920df68b725ee" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "proc-macro2" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rusqlite" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae" +dependencies = [ + "bitflags", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "syn" +version = "2.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..75f6141 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +resolver = "2" +members = [ + "calibre-db", +] diff --git a/calibre-db/Cargo.toml b/calibre-db/Cargo.toml new file mode 100644 index 0000000..5aea1bd --- /dev/null +++ b/calibre-db/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "calibre-db" +version = "0.1.0" +edition = "2021" + +[dependencies] +rusqlite = { version = "0.31.0", features = ["bundled"] } +thiserror = "1.0.59" diff --git a/calibre-db/src/calibre.rs b/calibre-db/src/calibre.rs new file mode 100644 index 0000000..b136c48 --- /dev/null +++ b/calibre-db/src/calibre.rs @@ -0,0 +1,94 @@ +use rusqlite::Connection; + +use crate::data::{author::Author, book::Book, error::DataStoreError, pagination::SortOrder}; + +pub struct Calibre { + conn: Connection, +} + +impl Calibre { + pub fn load(url: &str) -> Result { + let conn = Connection::open(url)?; + Ok(Self { conn }) + } + + pub fn books( + &self, + limit: u64, + cursor: Option<&str>, + sort_order: SortOrder, + ) -> Result, DataStoreError> { + Book::books(&self.conn, limit, cursor, sort_order) + } + + pub fn authors( + &self, + limit: u64, + cursor: Option<&str>, + sort_order: SortOrder, + ) -> Result, DataStoreError> { + Author::authors(&self.conn, limit, cursor, sort_order) + } + + pub fn author_books( + &self, + author_id: u64, + limit: u64, + cursor: Option<&str>, + sort_order: SortOrder, + ) -> Result, DataStoreError> { + Book::author_books(&self.conn, author_id, limit, cursor, sort_order) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn books() { + let c = Calibre::load("./testdata/metadata.db").unwrap(); + let books = c.books(10, None, SortOrder::ASC).unwrap(); + assert_eq!(books.len(), 4); + } + + #[test] + fn authors() { + let c = Calibre::load("./testdata/metadata.db").unwrap(); + let authors = c.authors(10, None, SortOrder::ASC).unwrap(); + assert_eq!(authors.len(), 3); + } + + #[test] + fn author_books() { + let c = Calibre::load("./testdata/metadata.db").unwrap(); + let books = c.author_books(1, 10, None, SortOrder::ASC).unwrap(); + assert_eq!(books.len(), 2); + } + + #[test] + fn pagination() { + let c = Calibre::load("./testdata/metadata.db").unwrap(); + let authors = c.authors(1, None, SortOrder::ASC).unwrap(); + assert_eq!(authors.len(), 1); + assert_eq!(authors[0].name, "Kevin R. Grazier"); + + let authors = c + .authors(1, Some(&authors[0].sort), SortOrder::ASC) + .unwrap(); + assert_eq!(authors.len(), 1); + assert_eq!(authors[0].name, "Terry Pratchett"); + + let authors = c + .authors(1, Some(&authors[0].sort), SortOrder::ASC) + .unwrap(); + assert_eq!(authors.len(), 1); + assert_eq!(authors[0].name, "Edward Noyes Westcott"); + + let authors = c + .authors(1, Some(&authors[0].sort), SortOrder::DESC) + .unwrap(); + assert_eq!(authors.len(), 1); + assert_eq!(authors[0].name, "Terry Pratchett"); + } +} diff --git a/calibre-db/src/data/author.rs b/calibre-db/src/data/author.rs new file mode 100644 index 0000000..106133d --- /dev/null +++ b/calibre-db/src/data/author.rs @@ -0,0 +1,38 @@ +use rusqlite::{Connection, Row}; + +use super::{ + error::DataStoreError, + pagination::{Pagination, SortOrder}, +}; + +#[derive(Debug)] +pub struct Author { + pub id: i32, + pub name: String, + pub sort: String, +} + +impl Author { + fn from_row(row: &Row<'_>) -> Result { + Ok(Self { + id: row.get(0)?, + name: row.get(1)?, + sort: row.get(2)?, + }) + } + + pub fn authors( + conn: &Connection, + limit: u64, + cursor: Option<&str>, + sort_order: SortOrder, + ) -> Result, DataStoreError> { + let pagination = Pagination::new("sort", cursor, limit, sort_order); + pagination.paginate( + conn, + "SELECT id, name, sort FROM authors", + &[], + Self::from_row, + ) + } +} diff --git a/calibre-db/src/data/book.rs b/calibre-db/src/data/book.rs new file mode 100644 index 0000000..91449c0 --- /dev/null +++ b/calibre-db/src/data/book.rs @@ -0,0 +1,56 @@ +use rusqlite::{Connection, Row}; + +use super::{ + error::DataStoreError, + pagination::{Pagination, SortOrder}, +}; + +#[derive(Debug)] +pub struct Book { + pub id: i32, + pub title: String, + pub sort: String, +} + +impl Book { + fn from_row(row: &Row<'_>) -> Result { + Ok(Self { + id: row.get(0)?, + title: row.get(1)?, + sort: row.get(2)?, + }) + } + + pub fn books( + conn: &Connection, + limit: u64, + cursor: Option<&str>, + sort_order: SortOrder, + ) -> Result, DataStoreError> { + let pagination = Pagination::new("sort", cursor, limit, sort_order); + pagination.paginate( + conn, + "SELECT id, title, sort FROM books", + &[], + Self::from_row, + ) + } + + pub fn author_books( + conn: &Connection, + author_id: u64, + limit: u64, + cursor: Option<&str>, + sort_order: SortOrder, + ) -> Result, DataStoreError> { + let pagination = Pagination::new("books.sort", cursor, limit, sort_order); + pagination.paginate( + conn, + "SELECT books.id, books.title, books.sort FROM books \ + INNER JOIN books_authors_link ON books.id = books_authors_link.book \ + WHERE books_authors_link.author = (:author_id) AND", + &[(":author_id", &author_id)], + Self::from_row, + ) + } +} diff --git a/calibre-db/src/data/error.rs b/calibre-db/src/data/error.rs new file mode 100644 index 0000000..8a2c803 --- /dev/null +++ b/calibre-db/src/data/error.rs @@ -0,0 +1,7 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum DataStoreError { + #[error("sqlite error")] + SqliteError(#[from] rusqlite::Error), +} diff --git a/calibre-db/src/data/pagination.rs b/calibre-db/src/data/pagination.rs new file mode 100644 index 0000000..6c7c90d --- /dev/null +++ b/calibre-db/src/data/pagination.rs @@ -0,0 +1,72 @@ +use rusqlite::{Connection, Row, ToSql}; + +use super::error::DataStoreError; + +#[derive(Debug, PartialEq)] +pub enum SortOrder { + ASC, + DESC, +} + +pub struct Pagination<'a> { + pub sort_col: &'a str, + pub limit: u64, + pub cursor: Option<&'a str>, + pub sort_order: SortOrder, +} + +impl<'a> Pagination<'a> { + pub fn new( + sort_col: &'a str, + cursor: Option<&'a str>, + limit: u64, + sort_order: SortOrder, + ) -> Self { + Self { + sort_col, + limit, + cursor, + sort_order, + } + } + + pub fn paginate( + &self, + conn: &Connection, + statement: &str, + params: &[(&str, &dyn ToSql)], + processor: F, + ) -> Result, DataStoreError> + where + F: FnMut(&Row<'_>) -> Result, + { + let cursor = self.cursor.unwrap_or(""); + let comparison = if self.sort_order == SortOrder::ASC { + ">" + } else { + "<" + }; + let where_sql = if statement.ends_with("AND") { + "" + } else { + "WHERE" + }; + + let sort_col = self.sort_col; + let sort_order = &self.sort_order; + // DANGER: vulnerable to SQL injection if statement or sort_col variable is influenced by user input + let mut stmt = conn.prepare(&format!( + "{statement} {where_sql} {sort_col} {comparison} (:cursor) ORDER BY {sort_col} {sort_order:?} LIMIT (:limit)" + ))?; + let params = [ + &[ + (":cursor", &cursor as &dyn ToSql), + (":limit", &self.limit as &dyn ToSql), + ], + params, + ] + .concat(); + let iter = stmt.query_map(params.as_slice(), processor)?; + Ok(iter.filter_map(Result::ok).collect()) + } +} diff --git a/calibre-db/src/lib.rs b/calibre-db/src/lib.rs new file mode 100644 index 0000000..2a70732 --- /dev/null +++ b/calibre-db/src/lib.rs @@ -0,0 +1,7 @@ +pub mod calibre; +pub mod data { + pub mod author; + pub mod book; + pub mod error; + pub mod pagination; +} diff --git a/calibre-db/testdata/metadata.db b/calibre-db/testdata/metadata.db new file mode 100644 index 0000000000000000000000000000000000000000..ef1842b7740cc35f99986f5b4f9a1ac096115dfe GIT binary patch literal 413696 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 L7h>rkZ}I;FA#!*A literal 0 HcmV?d00001 diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..b3f9457 --- /dev/null +++ b/flake.lock @@ -0,0 +1,145 @@ +{ + "nodes": { + "fenix": { + "inputs": { + "nixpkgs": "nixpkgs", + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1714544767, + "narHash": "sha256-kF1bX+YFMedf1g0PAJYwGUkzh22JmULtj8Rm4IXAQKs=", + "owner": "nix-community", + "repo": "fenix", + "rev": "73124e1356bde9411b163d636b39fe4804b7ca45", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "naersk": { + "inputs": { + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1713520724, + "narHash": "sha256-CO8MmVDmqZX2FovL75pu5BvwhW+Vugc7Q6ze7Hj8heI=", + "owner": "nix-community", + "repo": "naersk", + "rev": "c5037590290c6c7dae2e42e7da1e247e54ed2d49", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "naersk", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1714253743, + "narHash": "sha256-mdTQw2XlariysyScCv2tTE45QSU9v/ezLcHJ22f0Nxc=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "58a1abdbae3217ca6b702f03d3b35125d88a2994", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 0, + "narHash": "sha256-mdTQw2XlariysyScCv2tTE45QSU9v/ezLcHJ22f0Nxc=", + "path": "/nix/store/801l7gvdz7yaibhjsxqx82sc7zkakjbq-source", + "type": "path" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1714253743, + "narHash": "sha256-mdTQw2XlariysyScCv2tTE45QSU9v/ezLcHJ22f0Nxc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "58a1abdbae3217ca6b702f03d3b35125d88a2994", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "fenix": "fenix", + "flake-utils": "flake-utils", + "naersk": "naersk", + "nixpkgs": "nixpkgs_3" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1714501997, + "narHash": "sha256-g31zfxwUFzkPgX0Q8sZLcrqGmOxwjEZ/iqJjNx4fEGo=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "49e502b277a8126a9ad10c802d1aaa3ef1a280ef", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + }, + "systems": { + "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", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..d9f544f --- /dev/null +++ b/flake.nix @@ -0,0 +1,148 @@ +{ + description = "rusty-cops project"; + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + naersk.url = "github:nix-community/naersk"; + fenix.url = "github:nix-community/fenix"; + }; + + outputs = + { + self, + nixpkgs, + naersk, + fenix, + flake-utils, + ... + }: + let + buildTargets = { + "x86_64-linux" = { + crossSystemConfig = "x86_64-unknown-linux-musl"; + rustTarget = "x86_64-unknown-linux-musl"; + }; + + "i686-linux" = { + crossSystemConfig = "i686-unknown-linux-musl"; + rustTarget = "i686-unknown-linux-musl"; + }; + + "aarch64-linux" = { + crossSystemConfig = "aarch64-unknown-linux-musl"; + rustTarget = "aarch64-unknown-linux-musl"; + }; + + "armv6l-linux" = { + crossSystemConfig = "armv6l-unknown-linux-musleabihf"; + rustTarget = "arm-unknown-linux-musleabihf"; + }; + }; + + eachSystem = + supportedSystems: callback: + builtins.foldl' (overall: system: overall // { ${system} = callback system; }) { } supportedSystems; + + eachCrossSystem = + supportedSystems: callback: + eachSystem supportedSystems ( + buildSystem: + builtins.foldl' ( + inner: targetSystem: inner // { "cross-${targetSystem}" = callback buildSystem targetSystem; } + ) { default = callback buildSystem buildSystem; } supportedSystems + ); + + mkPkgs = + buildSystem: targetSystem: + import nixpkgs ( + { + system = buildSystem; + } + // ( + if targetSystem == null then + { } + else + { crossSystem.config = buildTargets.${targetSystem}.crossSystemConfig; } + ) + ); + in + flake-utils.lib.eachDefaultSystem ( + system: + let + pkgs = import nixpkgs { inherit system; }; + rust = fenix.packages.${system}.stable; + in + with pkgs; + { + devShells.default = mkShell { + buildInputs = [ + mosquitto + rust.toolchain + rust-analyzer + sea-orm-cli + ]; + }; + } + ) + // { + packages = eachCrossSystem (builtins.attrNames buildTargets) ( + buildSystem: targetSystem: + let + pkgs = mkPkgs buildSystem null; + pkgsCross = mkPkgs buildSystem targetSystem; + rustTarget = buildTargets.${targetSystem}.rustTarget; + + fenixPkgs = fenix.packages.${buildSystem}; + + mkToolchain = fenixPkgs: fenixPkgs.stable; + + toolchain = fenixPkgs.combine [ + (mkToolchain fenixPkgs).rustc + (mkToolchain fenixPkgs).cargo + (mkToolchain fenixPkgs.targets.${rustTarget}).rust-std + ]; + + buildPackageAttrs = + if builtins.hasAttr "makeBuildPackageAttrs" buildTargets.${targetSystem} then + buildTargets.${targetSystem}.makeBuildPackageAttrs pkgsCross + else + { }; + + naersk-lib = pkgs.callPackage naersk { + cargo = toolchain; + rustc = toolchain; + }; + in + naersk-lib.buildPackage ( + buildPackageAttrs + // rec { + src = ./.; + strictDeps = true; + doCheck = false; + + OPENSSL_STATIC = "1"; + OPENSSL_LIB_DIR = "${pkgsCross.pkgsStatic.openssl.out}/lib"; + OPENSSL_INCLUDE_DIR = "${pkgsCross.pkgsStatic.openssl.dev}/include"; + + # Required because ring crate is special. This also seems to have + # fixed some issues with the x86_64-windows cross-compile :shrug: + TARGET_CC = "${pkgsCross.stdenv.cc}/bin/${pkgsCross.stdenv.cc.targetPrefix}cc"; + + CARGO_BUILD_TARGET = rustTarget; + CARGO_BUILD_RUSTFLAGS = [ + "-C" + "target-feature=+crt-static" + + # -latomic is required to build openssl-sys for armv6l-linux, but + # it doesn't seem to hurt any other builds. + # "-C" + # "link-args=-static -latomic" + + "-C" + "linker=${TARGET_CC}" + ]; + } + ) + ); + }; +}