├── .envrc ├── .gitignore ├── Cargo.toml ├── nix-for-rust ├── src │ ├── bindings.rs │ ├── wrapper.h │ ├── main.rs │ ├── lib.rs │ ├── eval_cache │ │ ├── migrations │ │ │ └── 20241209_init.sql │ │ ├── mod.rs │ │ ├── db.rs │ │ └── trace.rs │ ├── error.rs │ ├── utils.rs │ ├── settings.rs │ ├── eval.rs │ ├── store.rs │ ├── flakes.rs │ ├── derivation.rs │ └── term.rs ├── Cargo.toml └── build.rs ├── nix-for-py ├── Cargo.toml └── src │ ├── function.rs │ ├── store.rs │ ├── nix_evaluator.rs │ ├── list.rs │ ├── attrset.rs │ └── lib.rs ├── README.org ├── flake.nix ├── flake.lock └── Cargo.lock /.envrc: -------------------------------------------------------------------------------- 1 | use flake 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .direnv 2 | target 3 | result -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["nix-for-rust", "nix-for-py"] 3 | resolver = "2" 4 | 5 | [profile.release] 6 | lto = "fat" 7 | codegen-units = 1 8 | -------------------------------------------------------------------------------- /nix-for-rust/src/bindings.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_upper_case_globals)] 2 | #![allow(non_camel_case_types)] 3 | #![allow(non_snake_case)] 4 | #![allow(dead_code)] 5 | include!(concat!(env!("OUT_DIR"), "/bindings.rs")); 6 | -------------------------------------------------------------------------------- /nix-for-rust/src/wrapper.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #define GC_THREADS 4 | #include 5 | #include 6 | #include 7 | #include 8 | -------------------------------------------------------------------------------- /nix-for-py/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nix-for-py" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | path = "src/lib.rs" 8 | crate-type = ["cdylib"] 9 | 10 | [dependencies] 11 | pyo3 = { version = "0.22.4", features = [ "anyhow", "extension-module" ] } 12 | nix-for-rust = { path = "../nix-for-rust", features = ["eval-cache", "derivation"] } 13 | anyhow = "1.0.84" -------------------------------------------------------------------------------- /nix-for-rust/src/main.rs: -------------------------------------------------------------------------------- 1 | use nix_for_rust::settings::NixSettings; 2 | use nix_for_rust::flakes::{FetchersSettings, FlakeLockFlags, FlakeRefSettings, FlakeSettings}; 3 | use nix_for_rust::term::Repr; 4 | 5 | pub fn main() -> anyhow::Result<()> { 6 | let mut settings = FlakeSettings::new(FetchersSettings::new()?)?; 7 | 8 | let mut flags = FlakeRefSettings::new(settings)?; 9 | flags.set_basedir(&std::env::current_dir()?)?; 10 | let flake_ref = flags.parse("..#hello.nixosConfigurations")?; 11 | 12 | let mut nix = NixSettings::default() 13 | // .with_flakes(settings) 14 | .with_default_store()?; 15 | 16 | let mut settings = FlakeSettings::new(FetchersSettings::new()?)?; 17 | 18 | let locked = nix.lock_flake(flake_ref, FlakeLockFlags::new(&settings)?)?; 19 | 20 | println!("{}", locked.outputs()?.repr()?); 21 | Ok(()) 22 | } 23 | -------------------------------------------------------------------------------- /nix-for-rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nix-for-rust" 3 | version = "0.1.0" 4 | edition = "2021" 5 | build = "build.rs" 6 | 7 | [build-dependencies] 8 | bindgen = "0.69.4" 9 | pkg-config = "0.3.30" 10 | 11 | [dependencies] 12 | anyhow = "1.0.83" 13 | futures-util = "0.3.31" 14 | home = "0.5.9" 15 | thiserror = "1.0.60" 16 | nix = { version = "0.30.1", features = [ "resource"] } 17 | blake3 = { version = "1.5.5", features = ["mmap"], optional = true } 18 | interprocess = { version = "2.2.2", optional = true } 19 | sqlx = { version = "0.8.2", features = [ "runtime-tokio", "sqlite", "migrate" ], optional = true} 20 | tokio = { version = "1.42.0", features = ["rt", "rt-multi-thread"], optional = true } 21 | nom = { version = "7.1.3", features = ["alloc"], optional = true } 22 | 23 | [features] 24 | default = [] 25 | eval-cache = ["dep:sqlx", "dep:blake3", "dep:tokio", "dep:interprocess", "nix/ptrace", "nix/fs", "nix/signal", "nix/process"] 26 | derivation = ["dep:nom"] 27 | -------------------------------------------------------------------------------- /nix-for-rust/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Nix For Rust 2 | //! 3 | //! Use nix values directly from rust. 4 | //! 5 | //! The entry point for nix evaluation is the [`NixSettings`][settings::NixSettings] builder, 6 | //! which builds a Nix evaluator with the provided settings. 7 | //! 8 | //! # Example 9 | //! ``` 10 | //! use nix_for_rust::settings::NixSettings; 11 | //! let mut state = NixSettings::default() 12 | //! .with_setting("experimental-features", "flakes") 13 | //! .with_default_store()?; 14 | //! ``` 15 | //! After passing the [nix store](https://nix.dev/manual/nix/2.17/command-ref/nix-store) path, it will return 16 | //! a [`NixEvalState`][eval::NixEvalState] instance, that can be used to evaluate code. 17 | 18 | pub mod eval; 19 | pub mod store; 20 | pub mod term; 21 | pub mod error; 22 | pub mod settings; 23 | pub mod flakes; 24 | mod bindings; 25 | mod utils; 26 | #[cfg(feature="eval-cache")] 27 | mod eval_cache; 28 | #[cfg(feature="derivation")] 29 | pub mod derivation; 30 | 31 | pub use utils::get_nix_version; 32 | -------------------------------------------------------------------------------- /nix-for-rust/src/eval_cache/migrations/20241209_init.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS evaluation_output ( 2 | id INTEGER NOT NULL PRIMARY KEY, 3 | main_file_path BLOB NOT NULL, 4 | accessor_path TEXT NOT NULL, 5 | output TEXT NOT NULL, 6 | main_file_hash CHAR(64) NOT NULL, 7 | input_hash CHAR(64) NOT NULL, 8 | -- reproducibility constraint, each of these pairs should always return the same result 9 | UNIQUE(input_hash, accessor_path, main_file_path, main_file_hash) 10 | ); 11 | 12 | CREATE INDEX IF NOT EXISTS idx_evaluation_output_accessor_path_file_path_file_hash ON evaluation_output(accessor_path, main_file_path, main_file_hash); 13 | 14 | CREATE TABLE IF NOT EXISTS evaluation_input ( 15 | evaluation_id INTEGER NOT NULL, 16 | file_path BLOB NOT NULL, 17 | FOREIGN KEY (evaluation_id) REFERENCES evaluation_output(id) 18 | ON UPDATE CASCADE ON DELETE CASCADE 19 | ); 20 | 21 | CREATE INDEX IF NOT EXISTS idx_evaluation_input_evaluation_id ON evaluation_input(evaluation_id); 22 | -------------------------------------------------------------------------------- /nix-for-py/src/function.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex, MutexGuard}; 2 | 3 | use nix_for_rust::term::{NixFunction, NixTerm}; 4 | use pyo3::{prelude::*, types::PyTuple}; 5 | 6 | use crate::{nix_term_to_py, PyTerm}; 7 | 8 | 9 | #[pyclass(frozen)] 10 | #[derive(Clone)] 11 | pub struct PyNixFunction(pub Arc>>); 12 | // Safety: we can only access the rawpointers through the Mutex, 13 | // which means that only one thread will have access to each at a time 14 | unsafe impl Send for PyNixFunction {} 15 | 16 | impl PyNixFunction { 17 | pub fn lock(&self) -> MutexGuard<'_, NixFunction<'static>> { 18 | self.0.lock().expect("Another thread panic'd while holding the mutex!") 19 | } 20 | } 21 | 22 | #[pymethods] 23 | impl PyNixFunction { 24 | #[pyo3(signature=(*args))] 25 | pub fn __call__(&self, args: &Bound<'_, PyTuple>) -> anyhow::Result { 26 | let function = self.0.lock().map_err(|e| anyhow::format_err!("{e}"))?; 27 | let mut ret = NixTerm::Function(function.clone()); 28 | for arg in args { 29 | let f: NixFunction = match ret { 30 | NixTerm::Function(f) => Ok::(f), 31 | _ => anyhow::bail!("Cannot call non-function argument") 32 | }?; 33 | ret = f.call_with(PyTerm(arg))?; 34 | } 35 | Python::with_gil(|py| nix_term_to_py(py, ret)) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /nix-for-py/src/store.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | use std::{collections::HashMap, path::PathBuf, sync::{Arc, Mutex, MutexGuard}}; 3 | use nix_for_rust::store::NixStore; 4 | use anyhow::Result; 5 | 6 | #[derive(Clone)] 7 | #[pyclass] 8 | pub struct PyNixStore(pub Arc>); 9 | unsafe impl Send for PyNixStore {} 10 | 11 | impl PyNixStore { 12 | fn lock(&self) -> MutexGuard<'_, &'static NixStore> { 13 | self.0.lock().expect("Another thread panic'd while holding the lock") 14 | } 15 | } 16 | 17 | #[pymethods] 18 | impl PyNixStore { 19 | 20 | pub fn version(&self) -> Result { 21 | self.lock().version() 22 | } 23 | 24 | pub fn build(&self, path: &str) -> Result> { 25 | let store = self.lock(); 26 | let path = store.parse_path(path)?; 27 | store.build(&path) 28 | .map_err(|e| e.into()) 29 | } 30 | 31 | pub fn is_valid_path(&self, path: &str) -> Result { 32 | let store = self.lock(); 33 | let path = store.parse_path(path)?; 34 | store.is_valid_path(&path) 35 | } 36 | 37 | pub fn store_dir(&self) -> Result { 38 | self.lock().store_dir() 39 | } 40 | 41 | pub fn copy_closure(&self, destination: &PyNixStore, path: &str) -> Result<()> { 42 | let store = self.lock(); 43 | let path = store.parse_path(path)?; 44 | let dst = destination.lock(); 45 | store.copy_closure(&dst, &path) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /nix-for-rust/build.rs: -------------------------------------------------------------------------------- 1 | extern crate bindgen; 2 | use std::env; 3 | use std::path::PathBuf; 4 | 5 | use bindgen::EnumVariation; 6 | 7 | #[derive(Debug)] 8 | struct StripNixPrefix {} 9 | impl bindgen::callbacks::ParseCallbacks for StripNixPrefix { 10 | fn item_name(&self, name: &str) -> Option { 11 | name.strip_prefix("nix_").map(String::from) 12 | } 13 | } 14 | 15 | fn main() { 16 | // Tell cargo to tell rustc to link the system bzip2 17 | // shared library. 18 | // println!("cargo:rustc-link-lib=nixutilc"); 19 | // println!("cargo:rustc-link-lib=nixstorec"); 20 | // println!("cargo:rustc-link-lib=nixexprc"); 21 | let nix_expr_c = pkg_config::probe_library("nix-expr-c").unwrap(); 22 | let nix_store_c = pkg_config::probe_library("nix-store-c").unwrap(); 23 | let nix_flake_c = pkg_config::probe_library("nix-flake-c").unwrap(); 24 | let gc = pkg_config::probe_library("bdw-gc").unwrap(); 25 | let bindings = bindgen::Builder::default() 26 | .clang_args(nix_expr_c.include_paths.iter().map(|path| format!("-I{}", path.to_string_lossy()))) 27 | .clang_args(nix_store_c.include_paths.iter().map(|path| format!("-I{}", path.to_string_lossy()))) 28 | .clang_args(nix_flake_c.include_paths.iter().map(|path| format!("-I{}", path.to_string_lossy()))) 29 | .clang_args(gc.include_paths.iter().map(|path| format!("-I{}", path.to_string_lossy()))) 30 | .header("src/wrapper.h") 31 | .parse_callbacks(Box::new(StripNixPrefix {})) 32 | .default_enum_style(EnumVariation::ModuleConsts) 33 | // Finish the builder and generate the bindings. 34 | .generate() 35 | // Unwrap the Result and panic on failure. 36 | .expect("Unable to generate bindings"); 37 | 38 | // Write the bindings to the $OUT_DIR/bindings.rs file. 39 | let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); 40 | bindings 41 | .write_to_file(out_path.join("bindings.rs")) 42 | .expect("Couldn't write bindings!"); 43 | } 44 | -------------------------------------------------------------------------------- /nix-for-py/src/nix_evaluator.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | use nix_for_rust::eval::NixEvalState; 3 | use std::sync::{Arc, Mutex, MutexGuard}; 4 | use crate::{nix_term_to_py, store::PyNixStore}; 5 | 6 | #[derive(Clone)] 7 | #[pyclass] 8 | pub struct PyEvalState(pub Arc>); 9 | 10 | // Safety: we can only access the rawpointers through the Mutex, 11 | // which means that only one thread will have access to each at a time 12 | unsafe impl Send for PyEvalState {} 13 | 14 | impl PyEvalState { 15 | fn lock(&self) -> MutexGuard<'_, &'static NixEvalState> { 16 | self.0.lock().expect("Another thread panic'd while holding the lock") 17 | } 18 | } 19 | 20 | #[pymethods] 21 | impl PyEvalState { 22 | 23 | #[getter] 24 | pub fn store(&self) -> anyhow::Result { 25 | Ok(PyNixStore(Arc::new(Mutex::new(&self.lock().store)))) 26 | } 27 | 28 | #[pyo3(signature=(string, cwd=None))] 29 | pub fn eval_string(&self, py: Python<'_>, string: &str, cwd: Option) -> anyhow::Result { 30 | let term = self.lock().eval_string(string, cwd.unwrap_or(std::env::current_dir()?))?; 31 | nix_term_to_py(py, term) 32 | } 33 | 34 | pub fn eval_file(&self, py:Python<'_>, file: std::path::PathBuf) -> anyhow::Result { 35 | let term = self.lock().eval_file(&file)?; 36 | nix_term_to_py(py, term) 37 | } 38 | 39 | // pub fn eval_flake(&self, py:Python<'_>, flake_path: &str) -> anyhow::Result { 40 | // let term = self.lock().eval_flake(flake_path)?; 41 | // nix_term_to_py(py, term) 42 | // } 43 | 44 | pub fn get_setting(&self, key: &str) -> Option { 45 | self.lock().settings.get_setting(key) 46 | } 47 | 48 | pub fn eval_attr_from_file(&self, file: std::path::PathBuf, accessor_path: Vec) -> anyhow::Result { 49 | self.lock().eval_attr_from_file(file, accessor_path) 50 | .map_err(|e| e.into()) 51 | } 52 | 53 | pub fn builtins(&self, py:Python<'_>) -> anyhow::Result { 54 | let term = self.lock().builtins()?; 55 | nix_term_to_py(py, term) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /nix-for-py/src/list.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex, MutexGuard}; 2 | use pyo3::prelude::*; 3 | use nix_for_rust::term::{NixList, NixListIterator, Repr}; 4 | use anyhow::Result; 5 | use crate::nix_term_to_py; 6 | 7 | #[pyclass(frozen)] 8 | #[derive(Clone)] 9 | pub struct PyNixList(pub Arc>>); 10 | #[pyclass] 11 | pub struct PyNixListIterator(Mutex>); 12 | 13 | // Safety: we can only access the rawpointers through the Mutex, 14 | // which means that only one thread will have access to each at a time 15 | unsafe impl Send for PyNixList {} 16 | unsafe impl Send for PyNixListIterator {} 17 | 18 | impl PyNixList { 19 | pub fn lock(&self) -> MutexGuard<'_, NixList<'static>> { 20 | self.0.lock().expect("Another thread panic'd while holding the mutex!") 21 | } 22 | } 23 | 24 | #[pymethods] 25 | impl PyNixList { 26 | 27 | fn __len__(&self) -> Result { 28 | let list = self.lock(); 29 | let len = list.len()?; 30 | Ok(len as usize) 31 | } 32 | 33 | fn __iter__(&self) -> Result { 34 | let list = self.lock(); 35 | let leaked = Box::leak(Box::new(list.clone())); 36 | let list_iter = leaked.iter()?; 37 | Ok(PyNixListIterator(Mutex::new(list_iter))) 38 | } 39 | 40 | fn __repr__(&self) -> Result { 41 | let list = self.lock(); 42 | let repr = list.repr()?; 43 | Ok(format!("")) 44 | } 45 | 46 | fn __getitem__(&self, py: Python, item: u32) -> Result { 47 | let list = self.lock(); 48 | let item = list.get_idx(item)?; 49 | nix_term_to_py(py, item) 50 | } 51 | } 52 | 53 | impl PyNixListIterator { 54 | fn lock(&self) -> Result>> { 55 | self.0.lock().map_err(|e| anyhow::format_err!("{e}")) 56 | } 57 | } 58 | 59 | #[pymethods] 60 | impl PyNixListIterator { 61 | 62 | fn __len__(&self) -> Result { 63 | let iterator = self.lock()?; 64 | Ok(iterator.len as usize) 65 | } 66 | 67 | fn __iter__(slf: PyRef<'_, Self>) -> PyRef { 68 | slf 69 | } 70 | 71 | fn __next__(&mut self, py: Python) -> Result> { 72 | let next = self.lock()?.next(); 73 | if let Some(term) = next { 74 | let term = term?; 75 | Ok(Some(nix_term_to_py(py, term)?)) 76 | } else { 77 | Ok(None) 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /nix-for-rust/src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{callback_get_result_string, callback_get_result_string_data}; 2 | use crate::bindings::{err, err_info_msg, err_msg, err_name}; 3 | use crate::store::NixContext; 4 | use std::fmt::Display; 5 | use std::ffi::{c_uint, CStr}; 6 | use thiserror::Error; 7 | 8 | #[derive(Error)] 9 | pub struct NixError { 10 | code: err::Type, 11 | msg: String, 12 | kind: NixErrorKind 13 | } 14 | 15 | #[derive(Debug, Error)] 16 | pub enum NixErrorKind { 17 | #[error("Unknown error")] 18 | UnknownError, 19 | #[error("An overflow has occured")] 20 | OverflowError, 21 | #[error("Key does not exist")] 22 | KeyError, 23 | #[error("{name}: {info_msg}")] 24 | GenericError { info_msg: String, name: String } 25 | } 26 | 27 | impl std::fmt::Debug for NixError { 28 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 29 | write!(f, "({}) {self}", self.code as u32) 30 | } 31 | } 32 | 33 | impl Display for NixError { 34 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 35 | let res = write!(f, "{}", self.kind); 36 | if !self.msg.is_empty() { 37 | write!(f, "\nNixInfo: {}", self.msg) 38 | } else { 39 | res 40 | } 41 | } 42 | } 43 | 44 | pub fn handle_nix_error(error: err::Type, ctx: &NixContext) -> NixError { 45 | let msg= unsafe { 46 | let mut len : c_uint = 0; 47 | let extra_ctx = NixContext::default(); 48 | let buf = err_msg(extra_ctx._ctx.as_ptr(), ctx._ctx.as_ptr(), &mut len as *mut c_uint); 49 | let c_str = CStr::from_ptr(buf); 50 | c_str.to_str().expect("Error msg is not a valid string").to_owned() 51 | }; 52 | let kind = match error { 53 | err::NIX_ERR_KEY => NixErrorKind::KeyError, 54 | err::NIX_ERR_OVERFLOW => NixErrorKind::OverflowError, 55 | err::NIX_ERR_UNKNOWN => NixErrorKind::UnknownError, 56 | err::NIX_ERR_NIX_ERROR => { 57 | let temp_ctx = NixContext::default(); 58 | let mut name: anyhow::Result = Err(anyhow::anyhow!("Nix C API didn't return string")); 59 | unsafe { 60 | err_name(temp_ctx._ctx.as_ptr(), ctx._ctx.as_ptr(), Some(callback_get_result_string), callback_get_result_string_data(&mut name)); 61 | } 62 | temp_ctx.check_call().expect("error thrown when reading error name"); 63 | let name = name.expect("Nix should always return valid strings"); 64 | let mut info_msg: anyhow::Result = Err(anyhow::anyhow!("Nix C API didn't return string")); 65 | unsafe { err_info_msg(temp_ctx._ctx.as_ptr(), ctx._ctx.as_ptr(), Some(callback_get_result_string), callback_get_result_string_data(&mut info_msg)) }; 66 | temp_ctx.check_call().expect("error thrown when reading error info"); 67 | let info_msg = info_msg.expect("Nix should always return valid strings"); 68 | NixErrorKind::GenericError { name, info_msg } 69 | } 70 | _otherwise => panic!("Unrecognized error code."), 71 | }; 72 | NixError { code: error, msg, kind } 73 | } 74 | -------------------------------------------------------------------------------- /nix-for-rust/src/eval_cache/mod.rs: -------------------------------------------------------------------------------- 1 | mod db; 2 | mod trace; 3 | 4 | use anyhow::Result; 5 | use interprocess::unnamed_pipe::Sender; 6 | use nix::sys::signal; 7 | use nix::unistd::{fork, ForkResult}; 8 | use trace::FileTracer; 9 | use std::io::{BufReader, BufRead, Write}; 10 | use std::path::{PathBuf, Path}; 11 | use nix::sys::ptrace; 12 | 13 | use crate::eval::NixEvalState; 14 | use crate::term::{NixTerm, Repr}; 15 | 16 | struct FileAttribute { 17 | path: PathBuf, 18 | hash: blake3::Hash, 19 | accessor_path: Vec 20 | } 21 | 22 | impl FileAttribute { 23 | pub fn new, I: IntoIterator, P: AsRef>(path: P, accessor_path: I) -> Result { 24 | Ok(FileAttribute { 25 | hash: blake3::hash(&std::fs::read(&path)?), 26 | path: std::fs::canonicalize(path)?, 27 | accessor_path: accessor_path.into_iter().map(|s| String::from(s.as_ref())).collect() 28 | }) 29 | } 30 | } 31 | 32 | impl NixEvalState { 33 | 34 | fn evaluate_traced<'s, S: AsRef, I: IntoIterator, P: AsRef>(&self, file: P, accessor_path: I, mut sender: Sender) { 35 | ptrace::traceme().unwrap(); 36 | signal::raise(signal::Signal::SIGSTOP).unwrap(); 37 | let path = accessor_path 38 | .into_iter() 39 | .fold(self.eval_file(file), |attr, accessor| attr.and_then(|term| term 40 | .get(accessor.as_ref()) 41 | .map_err(|e| anyhow::format_err!(e)))) 42 | .and_then(|term| match term { 43 | NixTerm::String(p) => Ok(p), 44 | other => Err(anyhow::format_err!("Attribute did not evaluate to string: '{}'", other.repr().unwrap())) 45 | }); 46 | match path { 47 | Ok(p) => { 48 | sender.write_all(p.as_bytes()).unwrap(); 49 | sender.write(b"\n").unwrap(); 50 | }, 51 | Err(e) => { 52 | sender.write_all(b"\n").unwrap(); 53 | eprintln!("{e}"); 54 | }, 55 | } 56 | } 57 | 58 | pub fn eval_attr_from_file, I: IntoIterator + Clone, P: AsRef>(&self, file: P, accessor_path: I) -> Result { 59 | let file_attribute = FileAttribute::new(&file, accessor_path.clone())?; 60 | if let Some(p) = db::query_attr_in_cache(&file_attribute)? { 61 | Ok(p) 62 | } else { 63 | let (sender, receiver) = interprocess::unnamed_pipe::pipe()?; 64 | match unsafe { fork()? } { 65 | ForkResult::Child => { 66 | self.evaluate_traced(file, accessor_path, sender); 67 | std::process::exit(0); 68 | } 69 | ForkResult::Parent { child } => { 70 | let tracer = FileTracer::new(); 71 | let input_files = tracer.watch(child)?; 72 | let mut output = String::new(); 73 | BufReader::new(receiver).read_line(&mut output)?; 74 | let out = output.trim(); 75 | if out.is_empty() { 76 | return Err(anyhow::format_err!("Error while evaluating expression")); 77 | }; 78 | db::insert_evaluation_output(&file_attribute, input_files.into_iter().collect(), &out)?; 79 | Ok(output) 80 | } 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /nix-for-rust/src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, ffi::{c_char, c_void, CStr}}; 2 | use anyhow::Result; 3 | use crate::bindings::version_get; 4 | 5 | pub fn get_nix_version() -> String { 6 | unsafe { 7 | let version = version_get(); 8 | CStr::from_ptr(version) 9 | .to_str() 10 | .expect("nix version should be valid utf8") 11 | .to_owned() 12 | } 13 | } 14 | 15 | // taken from https://github.com/nixops4/nixops4/blob/main/rust/nix-util/src/string_return.rs 16 | pub unsafe extern "C" fn callback_get_result_string( 17 | start: *const ::std::os::raw::c_char, 18 | n: std::os::raw::c_uint, 19 | user_data: *mut std::os::raw::c_void, 20 | ) { 21 | let ret = user_data as *mut Result; 22 | 23 | if start.is_null() { 24 | if n != 0 { 25 | panic!("callback_get_result_string: start is null but n is not zero"); 26 | } 27 | *ret = Ok(String::new()); 28 | return; 29 | } 30 | 31 | let slice = std::slice::from_raw_parts(start as *const u8, n as usize); 32 | 33 | if (*ret).is_ok() { 34 | panic!( 35 | "callback_get_result_string: Result must be initialized to Err. Did Nix call us twice?" 36 | ); 37 | } 38 | 39 | *ret = String::from_utf8(slice.to_vec()) 40 | .map_err(|e| anyhow::format_err!("Nix string is not valid UTF-8: {}", e)); 41 | } 42 | 43 | // taken from https://github.com/nixops4/nixops4/blob/main/rust/nix-util/src/string_return.rs 44 | pub fn callback_get_result_string_data(vec: &mut Result) -> *mut std::os::raw::c_void { 45 | vec as *mut Result as *mut std::os::raw::c_void 46 | } 47 | 48 | pub extern "C" fn read_into_hashmap(map: *mut c_void, outname: *const c_char, out: *const c_char) { 49 | let map: &mut HashMap = unsafe { &mut *(map as *mut std::collections::HashMap) }; 50 | let key = unsafe { CStr::from_ptr(outname)}.to_str().expect("nix key should be valid string"); 51 | let path = unsafe { CStr::from_ptr(out)}.to_str().expect("nix path should be valid string"); 52 | map.insert(key.to_string(), path.to_string()); 53 | } 54 | 55 | // pub unsafe extern "C" fn call_rust_closure( 56 | // func: *mut c_void, 57 | // context: *mut c_context, 58 | // state: *mut EvalState, 59 | // args: *mut *mut Value, 60 | // mut ret: *mut Value 61 | // ) 62 | // where F: Fn(NixTerm) -> Result { 63 | // let closure: &Box = std::mem::transmute(func); 64 | // let ctx = NixContext { _ctx: NonNull::new(context).expect("context should never be null") }; 65 | // let store = NixStore::new(ctx, ""); 66 | // let state = NonNull::new(state).expect("state should never be null"); 67 | // let value = { 68 | // value_force(state.store.ctx.ptr(), state.state_ptr(), *args); 69 | // NonNull::new(*args).expect("Expected at least one argument") 70 | // }; 71 | // state.store.ctx.check_call().unwrap(); 72 | // let rawvalue = RawValue { 73 | // value, 74 | // _state: state.clone() 75 | // }; 76 | // let argument: NixTerm = rawvalue.to_nix(&state).unwrap(); 77 | // let func_ret: NixTerm = closure(argument).expect("Closure returned an error"); 78 | // let rawvalue: RawValue = func_ret.to_raw_value(&state); 79 | // unsafe { 80 | // copy_value(state.store.ctx.ptr(), ret, rawvalue.value.as_ptr()); 81 | // } 82 | // state.store.ctx.check_call().unwrap() 83 | // } 84 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | * Nix Forall 2 | 3 | Nix FFI for all languages. This is a collection of packages built upon a solid interface for interacting with the nix evaluator in Rust, through the recently added [[https://github.com/NixOS/nix/pull/8699][Nix C-Api]], and a couple of wrapper libraries so that it can be seamlessly accessed from other languages as well. 4 | 5 | It is very much in alpha and constantly changing, so don't expect the API's to be stable as of now. Still, the general interface for all libraries should stay relatively intact: 6 | 1. Build a nix evaluator instance with given settings, where the default is an empty one. You may pass per-evaluator settings to customize behavior; for instance, like ~extra-experimental-features="flakes"~ is needed in order to call ~builtins.getFlake~, used inside ~state.eval_flake~. 7 | 2. Use said nix evaluator to load a nix file, flake or string. 8 | 3. Interact with said nix value. 9 | 10 | If it is a "simple" value, ie. string, number, float, it should translate to a builtin value, while lists, attribute sets and functions will have wrappers around them to implement some niceties, like lazy evaluation by default, special accessors and derivation =build()= helpers. 11 | 12 | Currently, only Rust and Python libraries are defined. 13 | 14 | #+begin_quote 15 | This crate uses [[https://github.com/oxalica/nocargo][nocargo]] to build directly through nix, without the use of cargo. If you want to use cargo, you may drop in the shell (through ~nix develop~). 16 | #+end_quote 17 | 18 | ** =nix-for-py/= 19 | =nix_for_py= exposes only one function, =nix_evaluator=, that is used to instantiate a nix evaluator, that will be used to evaluate nix code: 20 | 21 | #+begin_src python 22 | from nix_for_py import nix_evaluator 23 | 24 | evaluator = nix_evaluator(settings={'experimental-features':'flakes'}) 25 | nixpkgs = evaluator.eval_flake('github:nixos/nixpkgs') 26 | hello = nixpkgs.legacyPackages['x86_64-linux'].hello.build() 27 | print(hello) 28 | #+end_src 29 | 30 | which should print 31 | #+begin_src python 32 | {'out': '/nix/store/26xbg1ndr7hbcncrlf9nhx5is2b25d13-hello-2.12.1'} 33 | #+end_src 34 | 35 | *** Install 36 | The best way to utilize it is to use it through the =overlay= exposed by this flake, which will add the =nix_for_py= package to all python versions of nixpkgs. 37 | #+begin_src nix 38 | pkgs = import nixpkgs { 39 | overlays = [ nix-forall.overlays.${system}.default ]; 40 | }; 41 | #+end_src 42 | and then create a python instance with it: 43 | #+begin_src nix 44 | pkgs.mkShell { 45 | buildInputs = [ 46 | (pkgs.python3.withPackages (p: [ p.nix-for-py ])) 47 | ]; 48 | }; 49 | #+end_src 50 | 51 | ** =nix-for-rust/= 52 | The main way to invoke an evaluator is through the =NixSettings= builder. It lets you set settings which customize the behavior of the evaluator, and to finalize it, you must give it a store path, which will return the instance of the evaluator. 53 | #+begin_src rust 54 | use nix_for_rust::settings::NixSettings; 55 | 56 | pub fn main() -> anyhow::Result<()> { 57 | let mut state = NixSettings::default() 58 | .with_setting("experimental-features", "flakes") 59 | .with_default_store()?; 60 | let valid_pkgs = state.eval_flake("github:NixOS/nixpkgs")? 61 | .get("legacyPackages")? 62 | .get("x86_64-linux")? 63 | .items()? 64 | .filter_map(|(_name, term)| term.ok()) 65 | .count(); 66 | println!("Rejoice! You can build {valid_pkgs} packages from nixpkgs."); 67 | Ok(()) 68 | } 69 | #+end_src 70 | -------------------------------------------------------------------------------- /nix-for-rust/src/settings.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use anyhow::Result; 3 | use nix::sys::resource::{Resource, setrlimit, getrlimit}; 4 | 5 | use crate::eval::{NixEvalState, NixEvalStateBuilder}; 6 | use crate::bindings::{flake_settings_add_to_eval_state_builder, libexpr_init, libstore_init_no_load_config, setting_set}; 7 | use crate::flakes::FlakeSettings; 8 | use crate::store::{NixContext, NixStore}; 9 | 10 | pub struct NixSettings { 11 | pub settings: HashMap, 12 | pub store_params: HashMap, 13 | pub lookup_path: Vec, 14 | pub flake_settings: Option, 15 | pub stack_size: u64 16 | } 17 | 18 | fn set_stack_size(new_max: u64) -> nix::Result<()> { 19 | let (_soft_limit, hard_limit) = getrlimit(Resource::RLIMIT_STACK)?; 20 | setrlimit(Resource::RLIMIT_STACK, std::cmp::min(new_max, hard_limit), hard_limit) 21 | } 22 | 23 | impl Default for NixSettings { 24 | fn default() -> Self { 25 | NixSettings { 26 | stack_size: 64 * 1024 * 1024, // default stack size to 64MB 27 | settings: HashMap::default(), 28 | store_params: HashMap::default(), 29 | lookup_path: Vec::default(), 30 | flake_settings: None, 31 | } 32 | } 33 | } 34 | 35 | impl NixSettings { 36 | 37 | pub fn with_setting(mut self, key: &str, val: &str) -> Self { 38 | self.settings.insert(key.to_string(), val.to_string()); 39 | self 40 | } 41 | 42 | pub fn with_store_param(mut self, key: &str, val: &str) -> Self { 43 | self.store_params.insert(key.to_string(), val.to_string()); 44 | self 45 | } 46 | 47 | pub fn with_lookup_path(mut self, path: &str) -> Self { 48 | self.lookup_path.push(path.to_string()); 49 | self 50 | } 51 | 52 | pub fn with_default_store(self) -> Result { 53 | self.with_store("auto") 54 | } 55 | 56 | pub fn get_setting(&self, key: &str) -> Option { 57 | self.settings.get(key).map(String::from) 58 | } 59 | 60 | pub fn with_flakes(mut self, settings: FlakeSettings) -> Self { 61 | self.flake_settings = Some(settings); 62 | self 63 | } 64 | 65 | pub fn with_stack_size(mut self, size: u64) -> Self { 66 | self.stack_size = size; 67 | self 68 | } 69 | 70 | pub fn with_store(self, store_path: &str) -> Result { 71 | set_stack_size(self.stack_size)?; 72 | 73 | let ctx = NixContext::default(); 74 | unsafe { 75 | libstore_init_no_load_config(ctx.ptr()); 76 | ctx.check_call().expect("Couldn't initialize libstore"); 77 | libexpr_init(ctx.ptr()); 78 | ctx.check_call().expect("Couldn't initialize libexpr"); 79 | } 80 | 81 | let store = NixStore::new(ctx, store_path, self.store_params.clone())?; 82 | let mut state_builder = NixEvalStateBuilder::new(&store)?; 83 | 84 | if let Some(flake_settings) = &self.flake_settings { 85 | NixContext::checking(|ctx| unsafe { 86 | flake_settings_add_to_eval_state_builder(ctx.ptr(), flake_settings.settings_ptr.as_ptr(), state_builder.ptr.as_ptr()); 87 | })?; 88 | } 89 | 90 | for (key, val) in self.settings.iter() { 91 | NixContext::checking(|ctx| unsafe { 92 | setting_set(ctx.ptr(), key.as_ptr() as *const i8, val.as_ptr() as *const i8); 93 | })?; 94 | } 95 | 96 | state_builder.load_settings()?; 97 | 98 | state_builder.set_lookup_path(&self.lookup_path)?; 99 | 100 | Ok(NixEvalState { 101 | store, 102 | settings: self, 103 | _eval_state: state_builder.build()? 104 | }) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /nix-for-py/src/attrset.rs: -------------------------------------------------------------------------------- 1 | use std::{path::PathBuf, sync::{Arc, Mutex, MutexGuard}}; 2 | 3 | use nix_for_rust::term::{NixAttrSet, NixItemsIterator, NixNamesIterator, NixRealisedString, Repr}; 4 | use pyo3::{exceptions::PyAttributeError, prelude::*}; 5 | use anyhow::Result; 6 | 7 | use crate::nix_term_to_py; 8 | 9 | 10 | #[pyclass(frozen)] 11 | #[derive(Clone)] 12 | pub struct PyNixAttrSet(pub Arc>>); 13 | #[pyclass] 14 | pub struct PyNixNamesIterator(Mutex>); 15 | #[pyclass] 16 | pub struct PyNixItemsIterator(Mutex>); 17 | 18 | #[pyclass] 19 | pub struct PyNixRealisedString(Mutex>); 20 | 21 | // Safety: we can only access the rawpointers through the Mutex, 22 | // which means that only one thread will have access to each at a time 23 | unsafe impl Send for PyNixAttrSet {} 24 | unsafe impl Send for PyNixNamesIterator {} 25 | unsafe impl Send for PyNixItemsIterator {} 26 | unsafe impl Send for PyNixRealisedString {} 27 | 28 | impl PyNixAttrSet { 29 | pub fn lock(&self) -> MutexGuard<'_, NixAttrSet<'static>> { 30 | self.0.lock().expect("Another thread panic'd while holding the lock") 31 | } 32 | } 33 | 34 | #[pymethods] 35 | impl PyNixAttrSet { 36 | 37 | fn __getattr__(&self, py: Python, name: &str) -> Result { 38 | let attrset = self.lock(); 39 | let term = attrset.get(name).map_err(|_| PyAttributeError::new_err(name.to_string()))?; 40 | let obj = nix_term_to_py(py, term)?; 41 | Ok(obj) 42 | } 43 | 44 | fn realise(&self) -> Result { 45 | let attrset = self.lock(); 46 | let realised = attrset.realise()?; 47 | Ok(PyNixRealisedString(Mutex::new(realised))) 48 | } 49 | 50 | fn __getitem__(&self, py: Python, name: &str) -> Result { 51 | self.__getattr__(py, name) 52 | } 53 | 54 | fn __iter__(&self) -> Result { 55 | let attrset = self.lock(); 56 | let leaked = Box::leak(Box::new(attrset.clone())); 57 | let names_iter = leaked.names()?; 58 | Ok(PyNixNamesIterator(Mutex::new(names_iter))) 59 | } 60 | 61 | fn keys(&self) -> Result { 62 | self.__iter__() 63 | } 64 | 65 | fn __len__(&self) -> Result { 66 | let attrset = self.lock(); 67 | let len = attrset.len()?; 68 | Ok(len as usize) 69 | } 70 | 71 | fn items(&self) -> Result { 72 | let attrset = self.lock(); 73 | let leaked = Box::leak(Box::new(attrset.clone())); 74 | let items_iter = leaked.items()?; 75 | Ok(PyNixItemsIterator(Mutex::new(items_iter))) 76 | } 77 | 78 | fn __repr__(&self) -> Result { 79 | let attrset = self.lock(); 80 | let repr = attrset.repr()?; 81 | Ok(format!("")) 82 | } 83 | } 84 | 85 | 86 | #[pymethods] 87 | impl PyNixNamesIterator { 88 | fn __len__(&self) -> Result { 89 | let iterator = self.0.lock().expect("Another thread panic'd while holding the lock"); 90 | Ok(iterator.len as usize) 91 | } 92 | 93 | fn __iter__(slf: PyRef<'_, Self>) -> PyRef { 94 | slf 95 | } 96 | 97 | fn __next__(&mut self) -> PyResult> { 98 | let next = self.0.lock().expect("Another thread panic'd while holding the lock").next(); 99 | Ok(next) 100 | } 101 | } 102 | 103 | #[pymethods] 104 | impl PyNixItemsIterator { 105 | 106 | fn __len__(&self) -> Result { 107 | let iterator = self.0.lock().expect("Another thread panic'd while holding the lock"); 108 | Ok(iterator.len as usize) 109 | } 110 | 111 | fn __iter__(slf: PyRef<'_, Self>) -> PyRef { 112 | slf 113 | } 114 | 115 | fn __next__(&mut self, py: Python) -> Result> { 116 | let next = self.0.lock().expect("Another thread panic'd while holding the lock").next(); 117 | if let Some((name, term)) = next { 118 | let term = term?; 119 | Ok(Some((name, nix_term_to_py(py, term)?))) 120 | } else { 121 | Ok(None) 122 | } 123 | } 124 | } 125 | 126 | impl PyNixRealisedString { 127 | pub fn lock(&self) -> MutexGuard<'_, NixRealisedString<'static>> { 128 | self.0.lock().expect("Another thread panic'd while holding the lock") 129 | } 130 | } 131 | 132 | #[pymethods] 133 | impl PyNixRealisedString { 134 | 135 | #[getter] 136 | fn string(&self) -> String { 137 | self.lock().string.clone() 138 | } 139 | 140 | #[getter] 141 | fn paths(&self) -> Vec { 142 | self.lock() 143 | .paths 144 | .iter() 145 | .map(|p| p.path.clone()) 146 | .collect() 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /nix-for-py/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod attrset; 2 | mod list; 3 | mod function; 4 | mod nix_evaluator; 5 | mod store; 6 | 7 | use std::{collections::HashMap, sync::{Arc, Mutex}}; 8 | use attrset::PyNixAttrSet; 9 | use function::PyNixFunction; 10 | use list::PyNixList; 11 | use nix_evaluator::PyEvalState; 12 | use pyo3::{prelude::*, types::{PyList, PyDict}}; 13 | use nix_for_rust::{eval::NixEvalState, settings::NixSettings, term::{CollectToNix, NixAttrSet, NixEvalError, NixList, NixTerm, ToNix}}; 14 | use nix_for_rust::term::NixResult; 15 | 16 | fn nix_term_to_py(py: Python, term: NixTerm<'static>) -> anyhow::Result { 17 | match term { 18 | NixTerm::Null => Ok(py.None()), 19 | NixTerm::String(s) => Ok(s.into_py(py)), 20 | NixTerm::Int(i) => Ok(i.into_py(py)), 21 | NixTerm::Float(f) => Ok(f.into_py(py)), 22 | NixTerm::Bool(b) => Ok(b.into_py(py)), 23 | NixTerm::Path(p) => Ok(p.into_py(py)), 24 | NixTerm::List(list) => Ok(PyNixList(Arc::new(Mutex::new(list))).into_py(py)), 25 | NixTerm::Function(func) => Ok(PyNixFunction(Arc::new(Mutex::new(func))).into_py(py)), 26 | NixTerm::AttrSet(attrset) => Ok(PyNixAttrSet(Arc::new(Mutex::new(attrset))).into_py(py)), 27 | NixTerm::Thunk(thunk) => nix_term_to_py(py, thunk.force()?), 28 | NixTerm::External(_) => anyhow::bail!("Cannot turn external nix value to python yet."), 29 | } 30 | } 31 | 32 | struct PyTerm<'gil>(Bound<'gil, PyAny>); 33 | 34 | impl<'gil> ToNix<'static> for PyTerm<'gil> { 35 | fn to_nix(self, eval_state: &'static NixEvalState) -> NixResult> { 36 | let obj = self.0; 37 | if obj.is_none() { 38 | Ok(NixTerm::Null) 39 | } else if let Ok(i) = obj.extract::() { 40 | Ok(NixTerm::Int(i)) 41 | } else if let Ok(f) = obj.extract::() { 42 | Ok(NixTerm::Float(f)) 43 | } else if let Ok(s) = obj.extract::() { 44 | Ok(NixTerm::String(s)) 45 | } else if let Ok(b) = obj.extract::() { 46 | Ok(NixTerm::Bool(b)) 47 | } else if let Ok(l) = obj.downcast::() { 48 | let items: NixList = l 49 | .into_iter() 50 | .map(|p| PyTerm(p)) 51 | .collect_to_nix(eval_state)?; 52 | Ok(items.into()) 53 | } else if let Ok(d) = obj.downcast::() { 54 | let items: NixAttrSet = d.into_iter() 55 | .map(|(key, val)| { 56 | let Ok(key) = key.extract::() else { 57 | return Err(NixEvalError::TypeError { 58 | expected: "string".to_string(), 59 | got: key.get_type().name().expect("Name shouldn't throw error").to_string() 60 | }); 61 | }; 62 | Ok((key, PyTerm(val))) 63 | }) 64 | .collect_to_nix(eval_state)?; 65 | Ok(items.into()) 66 | } else if let Ok(d) = obj.extract::() { 67 | let attr = d.lock(); 68 | Ok(NixTerm::AttrSet(attr.clone())) 69 | } else if let Ok(d) = obj.extract::() { 70 | let list = d.lock(); 71 | Ok(NixTerm::List(list.clone())) 72 | } else if let Ok(d) = obj.extract::() { 73 | let func = d.lock(); 74 | Ok(NixTerm::Function(func.clone())) 75 | } else { 76 | Err(NixEvalError::TypeError { 77 | expected: "A Nix term".to_string(), 78 | got: obj.get_type().name().expect("Name shouldn't throw error").to_string() 79 | }) 80 | } 81 | } 82 | } 83 | 84 | #[pymodule] 85 | mod nix_for_py { 86 | use nix_for_rust::store::{NixContext, NixStore}; 87 | use store::PyNixStore; 88 | 89 | use super::*; 90 | 91 | #[pyfunction] 92 | #[pyo3(signature = (store="auto", lookup_path=None, store_params=None, settings=None, stack_size=None))] 93 | fn nix_evaluator( 94 | store: &str, 95 | lookup_path: Option>, 96 | store_params: Option>, 97 | settings: Option>, 98 | stack_size: Option, 99 | ) -> anyhow::Result { 100 | let nix_settings = NixSettings { 101 | settings: settings.unwrap_or_default(), 102 | store_params: store_params.unwrap_or_default(), 103 | lookup_path: lookup_path.unwrap_or_default(), 104 | stack_size: stack_size.unwrap_or(64 * 1024 * 1024), 105 | flake_settings: None 106 | }; 107 | let eval_state = nix_settings.with_store(store)?; 108 | Ok(PyEvalState(Arc::new(Mutex::new(Box::leak(Box::new(eval_state)))))) 109 | } 110 | 111 | #[pyfunction] 112 | #[pyo3(signature = (uri, **params))] 113 | fn store_open(uri: &str, params: Option>) -> anyhow::Result { 114 | let store = NixStore::new(NixContext::default(), uri, params.unwrap_or_default())?; 115 | Ok(PyNixStore(Arc::new(Mutex::new(Box::leak(Box::new(store)))))) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /nix-for-rust/src/eval_cache/db.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::OsStr; 2 | use std::str::FromStr; 3 | use std::path::{PathBuf, Path}; 4 | use sqlx::{Acquire, Pool, Sqlite}; 5 | use sqlx::{sqlite::{SqliteConnectOptions, SqliteJournalMode, SqliteSynchronous}, SqlitePool}; 6 | use anyhow::Result; 7 | use std::sync::LazyLock; 8 | use super::FileAttribute; 9 | use tokio::runtime::Runtime; 10 | 11 | static TOKIO_RT: LazyLock = LazyLock::new(|| { 12 | tokio::runtime::Builder::new_current_thread() 13 | .enable_time() 14 | .build() 15 | .expect("Could not initialize tokio runtime") 16 | }); 17 | 18 | static SQLITE_POOL: LazyLock> = LazyLock::new(|| { 19 | let cache_directory = home::home_dir() 20 | .unwrap_or_else(|| Path::new("/tmp").to_path_buf()) 21 | .join(".cache") 22 | .join("nix-for-rust") 23 | .join("eval-cache"); 24 | std::fs::create_dir_all(&cache_directory).expect("Could not create cache directory"); 25 | let sqlite_file = cache_directory.join("sqlite-v1.db"); 26 | let db_url = sqlite_file.as_path().to_string_lossy(); 27 | TOKIO_RT.block_on(async { setup_db(db_url).await.expect("Could not run database setup") }) 28 | }); 29 | 30 | pub fn query_attr_in_cache(file_attr: &FileAttribute) -> Result> { 31 | let pool = &*SQLITE_POOL; 32 | TOKIO_RT.block_on(async { 33 | query_evaluation_output(pool, file_attr).await 34 | }) 35 | } 36 | 37 | 38 | pub async fn setup_db>(database_url: P) -> Result { 39 | let conn_options = SqliteConnectOptions::from_str(database_url.as_ref())? 40 | .foreign_keys(true) 41 | .journal_mode(SqliteJournalMode::Wal) 42 | .synchronous(SqliteSynchronous::Normal) 43 | .pragma("mmap_size", "134217728") 44 | .create_if_missing(true); 45 | let pool = SqlitePool::connect_with(conn_options).await?; 46 | sqlx::migrate!("./src/eval_cache/migrations").run(&pool).await?; 47 | Ok(pool) 48 | } 49 | 50 | pub fn hash_files(files: &[PathBuf]) -> Result { 51 | let mut hasher = blake3::Hasher::new(); 52 | let mut files = files.to_vec(); 53 | files.sort(); 54 | for file in files { 55 | if file.is_dir() { 56 | for entry in std::fs::read_dir(file)? { 57 | hasher.update(entry?.path().as_os_str().as_encoded_bytes()); 58 | } 59 | } else { 60 | hasher.update_mmap(&file)?; 61 | } 62 | } 63 | Ok(hasher.finalize()) 64 | } 65 | 66 | pub async fn query_evaluation_output<'a, A>(conn: A, file_attr: &FileAttribute) -> Result> 67 | where 68 | A: Acquire<'a, Database = Sqlite>, 69 | { 70 | let mut conn = conn.acquire().await?; 71 | let eval_outputs: Vec<(i64, String, String)> = sqlx::query_as(r#" 72 | SELECT id, input_hash, output FROM evaluation_output 73 | WHERE main_file_path = ? AND main_file_hash = ? AND accessor_path = ? "#) 74 | .bind(file_attr.path.as_os_str().as_encoded_bytes()) 75 | .bind(file_attr.hash.to_string()) 76 | .bind(file_attr.accessor_path.join(".")) 77 | .fetch_all(&mut *conn) 78 | .await?; 79 | for (evaluation_id, input_hash, output) in eval_outputs { 80 | let files: Vec = sqlx::query_as("SELECT file_path FROM evaluation_input WHERE evaluation_id = ?") 81 | .bind(evaluation_id) 82 | .fetch_all(&mut *conn) 83 | .await? 84 | .into_iter() 85 | .map(|(path,): (Vec, )| unsafe { 86 | Path::new(OsStr::from_encoded_bytes_unchecked(&path)).to_path_buf() 87 | }) 88 | .collect(); 89 | let Ok(hash) = hash_files(&files) else { 90 | continue 91 | }; 92 | if hash.to_string() == input_hash { 93 | return Ok(Some(output)); 94 | } 95 | } 96 | Ok(None) 97 | } 98 | 99 | pub fn insert_evaluation_output(file_attr: &FileAttribute, input_files: Vec, output: &str) -> Result<()> { 100 | TOKIO_RT.block_on(async { 101 | let mut conn = SQLITE_POOL.acquire().await?; 102 | let input_hash = hash_files(&input_files)?; 103 | let (evaluation_id, ): (i64, ) = sqlx::query_as(r#" 104 | INSERT INTO evaluation_output (main_file_path, accessor_path, output, main_file_hash, input_hash) VALUES (?, ?, ?, ?, ?) 105 | RETURNING id 106 | "#) 107 | .bind(file_attr.path.as_os_str().as_encoded_bytes()) 108 | .bind(file_attr.accessor_path.join(".")) 109 | .bind(output) 110 | .bind(file_attr.hash.to_string()) 111 | .bind(input_hash.to_string()) 112 | .fetch_one(&mut *conn) 113 | .await?; 114 | for file in input_files { 115 | sqlx::query("INSERT INTO evaluation_input (evaluation_id, file_path) VALUES (?, ?)") 116 | .bind(evaluation_id) 117 | .bind(file.as_os_str().as_encoded_bytes()) 118 | .execute(&mut *conn) 119 | .await?; 120 | } 121 | Ok(()) 122 | }) 123 | } 124 | -------------------------------------------------------------------------------- /nix-for-rust/src/eval.rs: -------------------------------------------------------------------------------- 1 | use crate::bindings::{alloc_value, eval_state_build, eval_state_builder, eval_state_builder_free, eval_state_builder_load, eval_state_builder_new, eval_state_builder_set_lookup_path, expr_eval_from_string, libexpr_init, state_create, state_free, value_decref, value_incref, EvalState, Value}; 2 | use crate::settings::NixSettings; 3 | use crate::store::{NixContext, NixStore}; 4 | use crate::term::{NixEvalError, NixTerm, ToNix}; 5 | use std::path::PathBuf; 6 | use std::ptr::NonNull; 7 | use std::ffi::{c_char, CString}; 8 | use anyhow::Result; 9 | 10 | pub struct RawValue<'state> { 11 | pub _state: &'state NixEvalState, 12 | pub value: NonNull 13 | } 14 | 15 | pub struct ValueWrapper(pub NonNull); 16 | 17 | impl<'state> RawValue<'state> { 18 | pub fn empty(state: &'state NixEvalState) -> Self { 19 | let value = NixContext::non_null(|ctx| unsafe { 20 | alloc_value(ctx.ptr(), state.state_ptr()) 21 | }).expect("alloc_value should never return null"); 22 | RawValue { 23 | _state: state, 24 | value 25 | } 26 | } 27 | } 28 | 29 | impl<'state> Clone for RawValue<'state> { 30 | fn clone(&self) -> Self { 31 | NixContext::checking(|ctx| unsafe { 32 | value_incref(ctx.ptr(), self.value.as_ptr()) 33 | }).expect("error while increasing reference count"); 34 | RawValue { _state: &self._state, value: self.value } 35 | } 36 | } 37 | 38 | pub struct NixEvalState { 39 | pub store: NixStore, 40 | pub settings: NixSettings, 41 | pub _eval_state: NonNull 42 | } 43 | 44 | impl NixEvalState { 45 | 46 | pub fn state_ptr(&self) -> *mut EvalState { 47 | self._eval_state.as_ptr() 48 | } 49 | 50 | pub fn eval_string<'state>(&'state self, expr: &str, cwd: PathBuf) -> Result> { 51 | let cstr = CString::new(expr)?; 52 | let current_dir = cwd.as_path() 53 | .to_str() 54 | .ok_or_else(|| anyhow::format_err!("Cannot get current directory"))? 55 | .to_owned(); 56 | let current_dir = CString::new(current_dir)?; 57 | let val = RawValue::empty(self); 58 | unsafe { 59 | expr_eval_from_string( 60 | self.store.ctx.ptr(), 61 | self.state_ptr(), 62 | cstr.as_ptr(), 63 | current_dir.as_ptr(), 64 | val.value.as_ptr()); 65 | } 66 | self.store.ctx.check_call()?; 67 | val.to_nix(self).map_err(|err: NixEvalError| anyhow::anyhow!(err)) 68 | } 69 | 70 | pub fn eval_file<'state, P: AsRef>(&'state self, file: P) -> Result> { 71 | let contents = std::fs::read_to_string(&file)?; 72 | let realpath = std::fs::canonicalize(file)?; 73 | let cwd = if realpath.is_dir() { 74 | realpath 75 | } else { 76 | realpath.parent().map(|p| p.to_path_buf()).unwrap_or(realpath) 77 | }; 78 | self.eval_string(&contents, cwd) 79 | } 80 | 81 | pub fn builtins<'state>(&'state self) -> Result> { 82 | self.eval_string("builtins", std::env::current_dir()?) 83 | } 84 | 85 | } 86 | 87 | pub struct NixEvalStateBuilder { 88 | pub(crate) ptr: NonNull 89 | } 90 | 91 | impl NixEvalStateBuilder { 92 | pub fn new(store: &NixStore) -> Result { 93 | let ptr = NixContext::non_null(|ctx| unsafe { 94 | eval_state_builder_new(ctx.ptr(), store.store_ptr()) 95 | })?; 96 | Ok(NixEvalStateBuilder { ptr }) 97 | } 98 | 99 | pub fn load_settings(&mut self) -> Result<()> { 100 | NixContext::checking(|ctx| unsafe { 101 | eval_state_builder_load(ctx.ptr(), self.ptr.as_ptr()); 102 | }) 103 | } 104 | 105 | pub fn set_lookup_path(&mut self, lookup_path: &[String]) -> Result<()> { 106 | let mut lookup_path: Vec = lookup_path 107 | .iter() 108 | .map(|path| { Ok(CString::new(path.clone())?)}) 109 | .collect::>()?; 110 | let mut lookup_path: Vec<*const c_char> = lookup_path 111 | .iter_mut() 112 | .map(|p| p.as_ptr()) 113 | .chain(std::iter::once(std::ptr::null())) 114 | .collect(); 115 | NixContext::checking(|ctx| unsafe { 116 | eval_state_builder_set_lookup_path(ctx.ptr(), self.ptr.as_ptr(), lookup_path.as_mut_ptr()); 117 | }) 118 | } 119 | 120 | pub fn build(self) -> Result> { 121 | NixContext::checking(|ctx| unsafe { 122 | libexpr_init(ctx.ptr()); 123 | })?; 124 | let ptr = NixContext::non_null(|ctx| unsafe { 125 | eval_state_build(ctx.ptr(), self.ptr.as_ptr()) 126 | })?; 127 | Ok(ptr) 128 | } 129 | } 130 | 131 | impl Drop for NixEvalStateBuilder { 132 | fn drop(&mut self) { 133 | unsafe { 134 | eval_state_builder_free(self.ptr.as_ptr()); 135 | } 136 | } 137 | } 138 | 139 | impl Drop for NixEvalState { 140 | fn drop(&mut self) { 141 | unsafe { 142 | state_free(self.state_ptr()); 143 | } 144 | } 145 | } 146 | 147 | impl<'state> Drop for RawValue<'state> { 148 | fn drop(&mut self) { 149 | NixContext::checking(|ctx| unsafe { 150 | value_decref(ctx.ptr(), self.value.as_ptr()); 151 | }).expect("error while freeing a RawValue") 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "A very basic flake"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; 6 | flake-utils.url = "github:numtide/flake-utils"; 7 | nocargo = { 8 | url = "github:o-santi/nocargo"; 9 | inputs.nixpkgs.follows = "nixpkgs"; 10 | inputs.registry-crates-io.follows = "registry-crates-io"; 11 | }; 12 | registry-crates-io = { 13 | url = "github:rust-lang/crates.io-index"; 14 | flake = false; 15 | }; 16 | rust-overlay = { 17 | url = "github:oxalica/rust-overlay"; 18 | inputs.nixpkgs.follows = "nixpkgs"; 19 | }; 20 | nix.url = "github:nixos/nix"; 21 | }; 22 | 23 | outputs = inputs @ { nixpkgs, flake-utils, nocargo, rust-overlay, ... }: 24 | flake-utils.lib.eachDefaultSystem (system: let 25 | pkgs = import nixpkgs { 26 | overlays = [ (import rust-overlay) nix-for-py-overlay ]; 27 | inherit system; 28 | }; 29 | nix = inputs.nix.packages.${system}.nix.libs; 30 | nix-deps = with nix; [ nix-store-c nix-expr-c nix-util-c nix-flake-c ]; 31 | rust-tools = pkgs.rust-bin.stable.latest; 32 | rustPlatform = pkgs.makeRustPlatform { 33 | cargo = rust-tools.minimal; 34 | rustc = rust-tools.minimal; 35 | }; 36 | bindgen_gcc_args = '' 37 | echo "Extending BINDGEN_EXTRA_CLANG_ARGS with system include paths..." 2>&1 38 | BINDGEN_EXTRA_CLANG_ARGS="$${BINDGEN_EXTRA_CLANG_ARGS:-}" 39 | include_paths=$( 40 | echo | $NIX_CC_UNWRAPPED -v -E -x c - 2>&1 \ 41 | | awk '/#include <...> search starts here:/{flag=1;next} \ 42 | /End of search list./{flag=0} \ 43 | flag==1 {print $1}' 44 | ) 45 | for path in $include_paths; do 46 | echo " - $path" 2>&1 47 | BINDGEN_EXTRA_CLANG_ARGS="$BINDGEN_EXTRA_CLANG_ARGS -I$path" 48 | done 49 | ''; 50 | make-workspace-for-python = python: nocargo.lib.${system}.mkRustPackageOrWorkspace { 51 | src = ./.; 52 | rustc = rust-tools.minimal; 53 | buildCrateOverrides = with pkgs; { 54 | "pyo3-build-config 0.22.4 (registry+https://github.com/rust-lang/crates.io-index)" = old: { 55 | nativeBuildInputs = [ python ]; 56 | propagatedBuildInputs = [ python ]; 57 | }; 58 | "zerovec-derive 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)" = old: { 59 | procMacro = true; 60 | }; 61 | "yoke-derive 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = old: { 62 | procMacro = true; 63 | }; 64 | "zerofrom-derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = old: { 65 | procMacro = true; 66 | }; 67 | "doctest-file 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = old: { 68 | procMacro = true; 69 | }; 70 | "nix-for-rust" = old: { 71 | LIBCLANG_PATH = lib.makeLibraryPath [ libclang ]; 72 | buildInputs = [ ] ++ nix-deps; 73 | nativeBuildInputs = [ pkg-config pkgs.stdenv.cc ] ++ nix-deps; 74 | } // lib.optionalAttrs pkgs.stdenv.cc.isGNU { 75 | postConfigure = bindgen_gcc_args; 76 | BINDGEN_EXTRA_CLANG_ARGS="-x c++ -std=c++2a"; 77 | # Avoid cc wrapper, because we only need to add the compiler/"system" dirs 78 | NIX_CC_UNWRAPPED = "${pkgs.stdenv.cc.cc}/bin/gcc"; 79 | }; 80 | "nix-for-py" = old: { 81 | nativeBuildInputs = [ pkg-config ] ++ nix-deps; 82 | }; 83 | }; 84 | }; 85 | nix-for-py = { buildPythonPackage, lib, python, system, setuptools }: 86 | buildPythonPackage { 87 | pname = "nix_for_py"; 88 | version = "0.0.1"; 89 | pyproject = false; 90 | src = (make-workspace-for-python python).release.nix-for-py; 91 | doCheck = false; 92 | postInstall = '' 93 | mkdir -p $out/${python.sitePackages} 94 | cp $src/lib/*.so $out/${python.sitePackages}/nix_for_py.so 95 | ''; 96 | }; 97 | nix-for-py-overlay = self: super: { 98 | pythonPackagesExtensions = super.pythonPackagesExtensions ++ [(python-final: python-prev: { 99 | nix-for-py = python-final.callPackage nix-for-py {}; 100 | })]; 101 | }; 102 | ws = make-workspace-for-python pkgs.python312; 103 | in rec { 104 | apps.default = { 105 | type = "app"; 106 | program = "${packages.nix-for-rust.bin}/bin/nix_for_rust"; 107 | }; 108 | packages = { 109 | inherit (ws.release) nix-for-rust nix-for-py; 110 | default = packages.nix-for-rust.bin; 111 | }; 112 | overlays = rec { 113 | default = nix-for-py-overlay; 114 | inherit nix-for-py-overlay; 115 | }; 116 | devShells.default = with pkgs; mkShell { 117 | buildInputs = [ 118 | python3 119 | gdb 120 | pkg-config 121 | libclang 122 | (rust-tools.default.override { 123 | extensions = ["rust-src" "rust-analyzer"]; 124 | }) 125 | ] ++ nix-deps; 126 | nativeBuildInputs = [ 127 | rustPlatform.bindgenHook 128 | ]; 129 | }; 130 | }); 131 | } 132 | -------------------------------------------------------------------------------- /nix-for-rust/src/eval_cache/trace.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{HashSet, HashMap}; 2 | use std::ffi::{c_void, OsString}; 3 | use std::os::unix::ffi::OsStringExt; 4 | use std::path::{Path, PathBuf}; 5 | 6 | use home::home_dir; 7 | use anyhow::{Context, Result}; 8 | use nix::sys::signal::Signal; 9 | use nix::sys::wait::{wait, waitpid, WaitStatus}; 10 | use nix::sys::ptrace::{self, AddressType, Options}; 11 | use nix::unistd::Pid; 12 | 13 | enum FileAccess { 14 | OpenFile { path: PathBuf, out_fd: u64 }, 15 | FileRead { fd : u64 }, 16 | ListDir { fd: u64 } 17 | } 18 | 19 | fn wait_till_syscall_exit(pid: Pid) -> Result<()> { 20 | ptrace::syscall(pid, None).context("when executing a syscall")?; 21 | waitpid(pid, None).context("while waiting for syscall exit")?; 22 | Ok(()) 23 | } 24 | 25 | impl FileAccess { 26 | fn from_syscall(pid: Pid) -> Result> { 27 | let regs = ptrace::getregs(pid)?; 28 | let syscall = regs.orig_rax; 29 | match syscall { 30 | 0 => { 31 | let regs = ptrace::getregs(pid)?; 32 | Ok(Some(FileAccess::FileRead { fd: regs.rdi })) 33 | } 34 | 2 => { // open 35 | let path = read_path_from_register(pid, regs.rdi as *mut c_void); 36 | wait_till_syscall_exit(pid)?; 37 | let regs = ptrace::getregs(pid)?; 38 | Ok(Some(FileAccess::OpenFile { path, out_fd: regs.rax })) 39 | }, 40 | 257 => { // openat 41 | let path = read_path_from_register(pid, regs.rsi as *mut c_void); 42 | wait_till_syscall_exit(pid)?; 43 | let regs = ptrace::getregs(pid)?; 44 | Ok(Some(FileAccess::OpenFile { path, out_fd: regs.rax })) 45 | } 46 | 78 | 217 => { // getdents, getdents64 47 | let regs = ptrace::getregs(pid)?; 48 | wait_till_syscall_exit(pid)?; 49 | Ok(Some(FileAccess::ListDir { fd: regs.rdi })) 50 | } 51 | _ => { 52 | wait_till_syscall_exit(pid)?; 53 | Ok(None) 54 | } 55 | } 56 | } 57 | } 58 | 59 | pub struct FileTracer { 60 | read_files: HashSet, 61 | file_descriptors: HashMap, 62 | home_cache_dir: Option 63 | } 64 | 65 | impl FileTracer { 66 | pub fn new() -> Self { 67 | FileTracer { 68 | read_files: HashSet::new(), 69 | file_descriptors: HashMap::new(), 70 | home_cache_dir: home_dir().map(|mut p| { 71 | p.push(".cache"); 72 | p 73 | }) 74 | } 75 | } 76 | 77 | fn should_track_file(&self, path: &Path) -> bool { 78 | // /nix/store -> immutable, useless to track 79 | // /nix/var -> ephemeral, does not change results of evaluation. 80 | let is_nix = path.starts_with("/nix"); 81 | let is_git_cache = self.home_cache_dir.as_ref().map(|h| path.starts_with(h)).unwrap_or(true); 82 | let is_in_proc = path.starts_with("/proc"); 83 | !is_nix && !is_in_proc && !is_git_cache 84 | } 85 | 86 | fn handle_access(&mut self, access: FileAccess) { 87 | match access { 88 | FileAccess::OpenFile { path, out_fd: fd } => { 89 | self.file_descriptors.insert(fd, path); 90 | } 91 | FileAccess::ListDir { fd } | FileAccess::FileRead { fd } => { 92 | let path = self.file_descriptors.get(&fd).expect(&format!("unknown file descriptor '{fd}'")); 93 | if self.should_track_file(path) { 94 | self.read_files.insert(path.clone()); 95 | } 96 | } 97 | } 98 | } 99 | 100 | pub(crate) fn watch(mut self, child: Pid) -> Result> { 101 | wait().context("while attaching to child")?; 102 | ptrace::setoptions(child, Options::PTRACE_O_TRACESYSGOOD).context("setting ptrace options")?; 103 | ptrace::syscall(child, None).context("Exception thrown when executing syscall")?; 104 | loop { 105 | let status = wait().context("while waiting for syscall entry")?; 106 | match status { 107 | WaitStatus::Exited(_pid, _) => { 108 | break 109 | }, 110 | WaitStatus::Stopped(pid, sig @ (Signal::SIGCHLD | Signal::SIGWINCH)) => { 111 | ptrace::syscall(pid, Some(sig)).context("while resuming from a signal")?; 112 | } 113 | WaitStatus::Signaled(_pid, _signal, _) => { 114 | anyhow::bail!("Evaluation process was killed."); 115 | }, 116 | WaitStatus::PtraceSyscall(pid) => { 117 | // pre-syscall execution 118 | let access = FileAccess::from_syscall(pid) 119 | .context("while parsing syscall entry")?; 120 | // post-syscall execution 121 | if let Some(access) = access { 122 | self.handle_access(access); 123 | } 124 | // arrange for next syscall 125 | if let Err(_) = ptrace::syscall(pid, None) { 126 | break; 127 | }; 128 | } 129 | other => { 130 | unreachable!("Wait status '{other:?}' should never happen.") 131 | } 132 | } 133 | }; 134 | Ok(self.read_files) 135 | } 136 | 137 | } 138 | 139 | fn read_path_from_register(pid: Pid, address: AddressType) -> PathBuf { 140 | let mut bytes = Vec::new(); 141 | // Move 8 bytes up each time for next read. 142 | let mut count = 0; 143 | 'done: loop { 144 | let address = address.wrapping_add(count); 145 | 146 | let res: i64 = match ptrace::read(pid, address) { 147 | Ok(c_long) => c_long, 148 | Err(_) => break 'done, 149 | }; 150 | 151 | let bits = res.to_le_bytes(); 152 | 153 | if let Some(null_pos) = bits.iter().position(|&c| c == b'\0') { 154 | bytes.extend_from_slice(&bits[..null_pos]); 155 | break 'done 156 | } else { 157 | bytes.extend_from_slice(&bits); 158 | } 159 | 160 | count += size_of::(); 161 | } 162 | PathBuf::from(OsString::from_vec(bytes)) 163 | } 164 | -------------------------------------------------------------------------------- /nix-for-rust/src/store.rs: -------------------------------------------------------------------------------- 1 | use crate::error::{handle_nix_error, NixError}; 2 | use crate::term::NixEvalError; 3 | use crate::utils::{callback_get_result_string, callback_get_result_string_data, read_into_hashmap}; 4 | use crate::bindings::{c_context, c_context_create, err, err_code, libstore_init_no_load_config, store_copy_closure, store_free, store_get_storedir, store_get_version, store_is_valid_path, store_open, store_parse_path, store_path_free, store_path_name, store_real_path, store_realise, Store, StorePath}; 5 | use std::collections::HashMap; 6 | use std::ffi::{c_void, CString}; 7 | use std::os::raw::c_char; 8 | use std::path::{Path, PathBuf}; 9 | use std::ptr::{null_mut, NonNull}; 10 | use anyhow::Result; 11 | 12 | #[derive(Debug)] 13 | pub struct NixContext { 14 | pub(crate) _ctx: NonNull, 15 | } 16 | 17 | impl Default for NixContext { 18 | fn default() -> Self { 19 | let _ctx = unsafe { c_context_create() }; 20 | let _ctx = match NonNull::new(_ctx) { 21 | Some(c) => c, 22 | None => panic!("c_context_create returned null") 23 | }; 24 | NixContext { _ctx } 25 | } 26 | } 27 | 28 | impl NixContext { 29 | 30 | pub fn ptr(&self) -> *mut c_context { 31 | self._ctx.as_ptr() 32 | } 33 | 34 | pub fn check_call(&self) -> std::result::Result<(), NixError> { 35 | let err = unsafe { err_code(self._ctx.as_ptr())}; 36 | if err != err::NIX_OK as i32 { 37 | Err(handle_nix_error(err, self)) 38 | } else { 39 | Ok(()) 40 | } 41 | } 42 | 43 | pub fn checking T>(mut closure: F) -> Result { 44 | let ctx = Self::default(); 45 | let res = closure(&ctx); 46 | ctx.check_call()?; 47 | Ok(res) 48 | } 49 | 50 | pub fn non_null *mut T>(mut closure: F) -> Result> { 51 | let ctx = Self::default(); 52 | let res = closure(&ctx); 53 | ctx.check_call()?; 54 | 55 | NonNull::new(res) 56 | .ok_or(anyhow::format_err!("Nix C API returned a null pointer")) 57 | } 58 | } 59 | 60 | #[derive(Debug)] 61 | pub struct NixStore { 62 | pub ctx: NixContext, 63 | pub _store: NonNull 64 | } 65 | 66 | #[derive(Debug)] 67 | pub struct NixStorePath<'store> { 68 | pub path: PathBuf, 69 | pub store: &'store NixStore, 70 | pub(crate) _ptr: NonNull 71 | } 72 | 73 | impl NixStore { 74 | 75 | pub(crate) fn store_ptr(&self) -> *mut Store { 76 | self._store.as_ptr() 77 | } 78 | 79 | pub fn new, S: Into>>(ctx: NixContext, uri: &str, extra_params: I) -> Result { 80 | let uri = CString::new(uri)?; 81 | let _store = { 82 | let params: Vec<(CString, CString)> = extra_params 83 | .into_iter() 84 | .map(|(k, v)| Ok((CString::new(k)?, CString::new(v)?))) 85 | .collect::>()?; 86 | let mut params: Vec<[*const c_char; 2]> = params 87 | .iter() 88 | .map(|(k, v)| [k.as_ptr(), v.as_ptr()]) 89 | .collect(); 90 | let mut params: Vec<*mut *const c_char> = params 91 | .iter_mut() 92 | .map(|p| p.as_mut_ptr()) 93 | .chain(std::iter::once(null_mut())) 94 | .collect(); 95 | unsafe { 96 | libstore_init_no_load_config(ctx.ptr()); 97 | ctx.check_call()?; 98 | store_open(ctx._ctx.as_ptr(), uri.into_raw(), params.as_mut_ptr()) 99 | } 100 | }; 101 | ctx.check_call()?; 102 | let store = NonNull::new(_store).ok_or(anyhow::anyhow!("nix_store_open returned null"))?; 103 | Ok(NixStore { ctx, _store: store }) 104 | } 105 | 106 | pub fn version(&self) -> Result { 107 | let mut version_string : Result = Err(anyhow::anyhow!("Nix C API didn't return a string.")); 108 | unsafe { store_get_version(self.ctx._ctx.as_ptr(), self.store_ptr(), Some(callback_get_result_string), callback_get_result_string_data(&mut version_string)) }; 109 | self.ctx.check_call()?; 110 | version_string 111 | } 112 | 113 | pub fn parse_path(&self, path: &str) -> Result { 114 | let c_path = CString::new(path)?; 115 | let path_ptr = unsafe { 116 | store_parse_path(self.ctx._ctx.as_ptr(), self.store_ptr(), c_path.as_ptr()) 117 | }; 118 | self.ctx.check_call()?; 119 | Ok(NixStorePath { 120 | store: self, 121 | path: Path::new(path).to_path_buf(), 122 | _ptr: NonNull::new(path_ptr) 123 | .ok_or_else(|| anyhow::format_err!("store_parse_path returned null"))? 124 | }) 125 | } 126 | 127 | pub fn build<'store>(&self, path: &NixStorePath<'store>) -> Result, NixEvalError> { 128 | let mut map = HashMap::new(); 129 | unsafe { 130 | store_realise( 131 | self.ctx._ctx.as_ptr(), 132 | self.store_ptr(), 133 | path.as_ptr(), 134 | &mut map as *mut HashMap as *mut c_void, 135 | Some(read_into_hashmap) 136 | ); 137 | } 138 | self.ctx.check_call()?; 139 | Ok(map) 140 | } 141 | 142 | pub fn is_valid_path(&self, path: &NixStorePath) -> Result { 143 | let is_valid = unsafe { 144 | store_is_valid_path(self.ctx.ptr(), self.store_ptr(), path.as_ptr()) 145 | }; 146 | self.ctx.check_call()?; 147 | Ok(is_valid) 148 | } 149 | 150 | pub fn store_dir(&self) -> Result { 151 | let mut dir_string : Result = Err(anyhow::anyhow!("Nix C API didn't return a string.")); 152 | unsafe { 153 | store_get_storedir(self.ctx.ptr(), self.store_ptr(), Some(callback_get_result_string), callback_get_result_string_data(&mut dir_string)); 154 | } 155 | self.ctx.check_call()?; 156 | dir_string.map(PathBuf::from) 157 | } 158 | 159 | pub fn copy_closure(&self, destination: &NixStore, path: &NixStorePath) -> Result<()> { 160 | unsafe { 161 | store_copy_closure(self.ctx.ptr(), self.store_ptr(), destination.store_ptr(), path.as_ptr()); 162 | } 163 | self.ctx.check_call() 164 | .map_err(|e| e.into()) 165 | } 166 | 167 | } 168 | 169 | impl<'store> NixStorePath<'store> { 170 | 171 | pub fn from_ptr(store: &'store NixStore, store_path: *mut StorePath) -> Result { 172 | let ctx = NixContext::default(); 173 | let mut path : Result = Err(anyhow::anyhow!("Nix C API didn't return a string.")); 174 | unsafe { 175 | store_real_path(ctx.ptr(), store.store_ptr(), store_path, Some(callback_get_result_string), callback_get_result_string_data(&mut path)) 176 | }; 177 | ctx.check_call()?; 178 | Ok(NixStorePath { 179 | store, 180 | path: PathBuf::from(path?), 181 | _ptr: NonNull::new(store_path) 182 | .ok_or_else(|| anyhow::format_err!("store_real_path returned null"))? 183 | }) 184 | } 185 | 186 | fn as_ptr(&self) -> *mut StorePath { 187 | self._ptr.as_ptr() 188 | } 189 | 190 | pub fn name(&self) -> Result { 191 | let mut name : Result = Err(anyhow::anyhow!("Nix C API didn't return a string.")); 192 | unsafe { 193 | store_path_name( 194 | self.as_ptr(), 195 | Some(callback_get_result_string), 196 | callback_get_result_string_data(&mut name)) 197 | }; 198 | name 199 | } 200 | } 201 | 202 | impl Drop for NixStore { 203 | fn drop(&mut self) { 204 | unsafe { 205 | store_free(self.store_ptr()); 206 | } 207 | } 208 | } 209 | 210 | impl<'store> Drop for NixStorePath<'store> { 211 | fn drop(&mut self) { 212 | unsafe { 213 | store_path_free(self.as_ptr()); 214 | } 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /nix-for-rust/src/flakes.rs: -------------------------------------------------------------------------------- 1 | use std::{path::Path, ptr::{null_mut, NonNull}}; 2 | use anyhow::Result; 3 | use nix::NixPath; 4 | 5 | use crate::bindings::{fetchers_settings, fetchers_settings_free, fetchers_settings_new, flake_lock, flake_lock_flags, flake_lock_flags_add_input_override, flake_lock_flags_free, flake_lock_flags_new, flake_lock_flags_set_mode_check, flake_lock_flags_set_mode_virtual, flake_reference, flake_reference_and_fragment_from_string, flake_reference_free, flake_reference_parse_flags, flake_reference_parse_flags_new, flake_reference_parse_flags_set_base_directory, flake_settings, flake_settings_free, flake_settings_new, locked_flake, locked_flake_free, locked_flake_get_output_attrs, setting_set}; 6 | use crate::{eval::{NixEvalState, RawValue}, store::NixContext, term::{NixTerm, ToNix}, utils::{callback_get_result_string, callback_get_result_string_data}}; 7 | 8 | #[derive(Clone)] 9 | pub struct FetchersSettings { 10 | ptr: NonNull 11 | } 12 | 13 | impl FetchersSettings { 14 | pub fn new() -> Result { 15 | let ptr = NixContext::non_null(|ctx| unsafe { 16 | fetchers_settings_new(ctx.ptr()) 17 | })?; 18 | Ok(FetchersSettings { ptr }) 19 | } 20 | } 21 | 22 | impl Drop for FetchersSettings { 23 | fn drop(&mut self) { 24 | unsafe { 25 | fetchers_settings_free(self.ptr.as_mut()); 26 | } 27 | } 28 | } 29 | 30 | pub struct FlakeRefSettings { 31 | ptr: NonNull, 32 | pub settings: FlakeSettings, 33 | } 34 | 35 | impl FlakeRefSettings { 36 | pub fn new(settings: FlakeSettings) -> Result { 37 | let ptr = NixContext::non_null(move |ctx| unsafe { 38 | flake_reference_parse_flags_new(ctx.ptr(), settings.settings_ptr.as_ptr()) 39 | })?; 40 | Ok(FlakeRefSettings { ptr, settings }) 41 | } 42 | 43 | pub fn set_basedir(&mut self, dir: &Path) -> Result<()> { 44 | let dir_len = dir.len(); 45 | let dir_bytes = dir.as_os_str().as_encoded_bytes().as_ptr(); 46 | NixContext::checking(|ctx| unsafe { 47 | flake_reference_parse_flags_set_base_directory( 48 | ctx.ptr(), 49 | self.ptr.as_mut(), 50 | dir_bytes as *const i8, 51 | dir_len); 52 | }) 53 | } 54 | 55 | pub fn parse(self, uri: &str) -> Result { 56 | let mut fragment: Result = Err(anyhow::anyhow!("Nix C API didn't return a string.")); 57 | let mut ptr: *mut flake_reference = null_mut(); 58 | NixContext::checking(|ctx| unsafe { 59 | flake_reference_and_fragment_from_string( 60 | ctx.ptr(), 61 | self.settings.fetchers_settings.ptr.as_ptr(), 62 | self.settings.settings_ptr.as_ptr(), 63 | self.ptr.as_ptr(), 64 | uri.as_ptr() as *const i8, 65 | uri.len(), 66 | &mut ptr, 67 | Some(callback_get_result_string), 68 | callback_get_result_string_data(&mut fragment)) 69 | })?; 70 | let ref_ptr = NonNull::new(ptr) 71 | .ok_or(anyhow::format_err!("flake_reference_and_fragment_from_string returned null"))?; 72 | Ok(FlakeRef { 73 | settings: self, 74 | fragment: fragment?, 75 | ref_ptr 76 | }) 77 | } 78 | } 79 | 80 | pub struct FlakeRef { 81 | ref_ptr: NonNull, 82 | pub settings: FlakeRefSettings, 83 | pub fragment: String, 84 | } 85 | 86 | impl Drop for FlakeRef { 87 | fn drop(&mut self) { 88 | unsafe { 89 | flake_reference_free(self.ref_ptr.as_mut()) 90 | } 91 | } 92 | } 93 | 94 | #[derive(Clone)] 95 | pub struct FlakeSettings { 96 | pub(crate) settings_ptr: NonNull, 97 | pub(crate) fetchers_settings: FetchersSettings 98 | } 99 | 100 | impl FlakeSettings { 101 | pub fn new(fetchers_settings: FetchersSettings) -> Result { 102 | let settings_ptr = NixContext::non_null(|ctx| unsafe { 103 | 104 | flake_settings_new(ctx.ptr()) 105 | })?; 106 | // TODO: is this really necessary? doesn't make much sense to me 107 | NixContext::checking(|ctx| unsafe { 108 | let key = "experimental-features".to_string(); 109 | let val = "flakes".to_string(); 110 | setting_set(ctx.ptr(), key.as_ptr() as *const i8, val.as_ptr() as *const i8) 111 | })?; 112 | Ok(FlakeSettings { settings_ptr, fetchers_settings }) 113 | } 114 | } 115 | 116 | impl Drop for FlakeSettings { 117 | fn drop(&mut self) { 118 | unsafe { 119 | flake_settings_free(self.settings_ptr.as_ptr()); 120 | } 121 | } 122 | } 123 | 124 | pub struct FlakeLockFlags { 125 | ptr: NonNull 126 | } 127 | 128 | impl FlakeLockFlags { 129 | pub fn new(flake_settings: &FlakeSettings) -> Result { 130 | let ptr = NixContext::non_null(|ctx| unsafe { 131 | flake_lock_flags_new(ctx.ptr(), flake_settings.settings_ptr.as_ptr()) 132 | })?; 133 | Ok(FlakeLockFlags { ptr }) 134 | } 135 | 136 | pub fn check_up_to_date(&mut self) -> Result<()> { 137 | NixContext::checking(|ctx| unsafe { 138 | flake_lock_flags_set_mode_check(ctx.ptr(), self.ptr.as_ptr()); 139 | }) 140 | } 141 | 142 | pub fn update_in_memory(&mut self) -> Result<()> { 143 | NixContext::checking(|ctx| unsafe { 144 | flake_lock_flags_set_mode_virtual(ctx.ptr(), self.ptr.as_ptr()); 145 | }) 146 | } 147 | 148 | pub fn write_as_needed(&mut self) -> Result<()> { 149 | NixContext::checking(|ctx| unsafe { 150 | flake_lock_flags_set_mode_check(ctx.ptr(), self.ptr.as_ptr()); 151 | }) 152 | } 153 | 154 | pub fn add_input_override(&mut self, input_path: &str, flake_ref: &FlakeRef) -> Result<()> { 155 | NixContext::checking(|ctx| unsafe { 156 | flake_lock_flags_add_input_override(ctx.ptr(), self.ptr.as_ptr(), input_path.as_ptr() as *const i8, flake_ref.ref_ptr.as_ptr()); 157 | }) 158 | } 159 | 160 | } 161 | 162 | impl Drop for FlakeLockFlags { 163 | fn drop(&mut self) { 164 | unsafe { 165 | flake_lock_flags_free(self.ptr.as_ptr()); 166 | } 167 | } 168 | } 169 | 170 | 171 | pub struct LockedFlake<'state> { 172 | ptr: NonNull, 173 | state: &'state NixEvalState, 174 | pub flags: FlakeLockFlags, 175 | pub flake_ref: FlakeRef 176 | } 177 | 178 | impl<'state> LockedFlake<'state> { 179 | pub fn outputs(&self) -> Result { 180 | let value = NixContext::non_null(|ctx| unsafe { 181 | locked_flake_get_output_attrs( 182 | ctx.ptr(), 183 | self.flake_ref.settings.settings.settings_ptr.as_ptr(), 184 | self.state.state_ptr(), 185 | self.ptr.as_ptr()) 186 | })?; 187 | let raw_value = RawValue { 188 | _state: self.state, 189 | value 190 | }; 191 | raw_value.to_nix(&self.state) 192 | .map_err(|e| e.into()) 193 | } 194 | } 195 | 196 | impl<'state> Drop for LockedFlake<'state> { 197 | fn drop(&mut self) { 198 | unsafe { 199 | locked_flake_free(self.ptr.as_ptr()); 200 | } 201 | } 202 | } 203 | 204 | impl NixEvalState { 205 | 206 | pub fn flake_settings(&self) -> Result<&FlakeSettings> { 207 | self.settings.flake_settings 208 | .as_ref() 209 | .ok_or(anyhow::format_err!("NixEvalState was not initialized with flakes enabled.")) 210 | } 211 | 212 | pub fn lock_flake<'state>(&'state self, flake_ref: FlakeRef, lock_flags: FlakeLockFlags) -> Result> { 213 | let ptr = NixContext::non_null(|ctx| unsafe { 214 | flake_lock( 215 | ctx.ptr(), 216 | flake_ref.settings.settings.fetchers_settings.ptr.as_ptr(), 217 | flake_ref.settings.settings.settings_ptr.as_ptr(), 218 | self.state_ptr(), 219 | lock_flags.ptr.as_ptr(), 220 | flake_ref.ref_ptr.as_ptr()) 221 | })?; 222 | Ok(LockedFlake { ptr, flags: lock_flags, flake_ref, state: self }) 223 | } 224 | 225 | } 226 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-compat": { 4 | "flake": false, 5 | "locked": { 6 | "lastModified": 1733328505, 7 | "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", 8 | "owner": "edolstra", 9 | "repo": "flake-compat", 10 | "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", 11 | "type": "github" 12 | }, 13 | "original": { 14 | "owner": "edolstra", 15 | "repo": "flake-compat", 16 | "type": "github" 17 | } 18 | }, 19 | "flake-parts": { 20 | "inputs": { 21 | "nixpkgs-lib": [ 22 | "nix", 23 | "nixpkgs" 24 | ] 25 | }, 26 | "locked": { 27 | "lastModified": 1733312601, 28 | "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=", 29 | "owner": "hercules-ci", 30 | "repo": "flake-parts", 31 | "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9", 32 | "type": "github" 33 | }, 34 | "original": { 35 | "owner": "hercules-ci", 36 | "repo": "flake-parts", 37 | "type": "github" 38 | } 39 | }, 40 | "flake-utils": { 41 | "inputs": { 42 | "systems": "systems" 43 | }, 44 | "locked": { 45 | "lastModified": 1710146030, 46 | "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", 47 | "owner": "numtide", 48 | "repo": "flake-utils", 49 | "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", 50 | "type": "github" 51 | }, 52 | "original": { 53 | "owner": "numtide", 54 | "repo": "flake-utils", 55 | "type": "github" 56 | } 57 | }, 58 | "flake-utils_2": { 59 | "inputs": { 60 | "systems": "systems_2" 61 | }, 62 | "locked": { 63 | "lastModified": 1701680307, 64 | "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", 65 | "owner": "numtide", 66 | "repo": "flake-utils", 67 | "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", 68 | "type": "github" 69 | }, 70 | "original": { 71 | "owner": "numtide", 72 | "repo": "flake-utils", 73 | "type": "github" 74 | } 75 | }, 76 | "git-hooks-nix": { 77 | "inputs": { 78 | "flake-compat": [ 79 | "nix" 80 | ], 81 | "gitignore": [ 82 | "nix" 83 | ], 84 | "nixpkgs": [ 85 | "nix", 86 | "nixpkgs" 87 | ], 88 | "nixpkgs-stable": [ 89 | "nix", 90 | "nixpkgs" 91 | ] 92 | }, 93 | "locked": { 94 | "lastModified": 1734279981, 95 | "narHash": "sha256-NdaCraHPp8iYMWzdXAt5Nv6sA3MUzlCiGiR586TCwo0=", 96 | "owner": "cachix", 97 | "repo": "git-hooks.nix", 98 | "rev": "aa9f40c906904ebd83da78e7f328cd8aeaeae785", 99 | "type": "github" 100 | }, 101 | "original": { 102 | "owner": "cachix", 103 | "repo": "git-hooks.nix", 104 | "type": "github" 105 | } 106 | }, 107 | "nix": { 108 | "inputs": { 109 | "flake-compat": "flake-compat", 110 | "flake-parts": "flake-parts", 111 | "git-hooks-nix": "git-hooks-nix", 112 | "nixpkgs": "nixpkgs", 113 | "nixpkgs-23-11": "nixpkgs-23-11", 114 | "nixpkgs-regression": "nixpkgs-regression" 115 | }, 116 | "locked": { 117 | "lastModified": 1748472379, 118 | "narHash": "sha256-BMcxHC9EVBIbcRzhBd1O19CDOhkbprPSRUyLKbgCKWs=", 119 | "owner": "nixos", 120 | "repo": "nix", 121 | "rev": "ba960675354347f9f3cd833da9b519ecbab7170c", 122 | "type": "github" 123 | }, 124 | "original": { 125 | "owner": "nixos", 126 | "repo": "nix", 127 | "type": "github" 128 | } 129 | }, 130 | "nix-filter": { 131 | "locked": { 132 | "lastModified": 1687178632, 133 | "narHash": "sha256-HS7YR5erss0JCaUijPeyg2XrisEb959FIct3n2TMGbE=", 134 | "owner": "numtide", 135 | "repo": "nix-filter", 136 | "rev": "d90c75e8319d0dd9be67d933d8eb9d0894ec9174", 137 | "type": "github" 138 | }, 139 | "original": { 140 | "owner": "numtide", 141 | "repo": "nix-filter", 142 | "type": "github" 143 | } 144 | }, 145 | "nixpkgs": { 146 | "locked": { 147 | "lastModified": 1747179050, 148 | "narHash": "sha256-qhFMmDkeJX9KJwr5H32f1r7Prs7XbQWtO0h3V0a0rFY=", 149 | "owner": "NixOS", 150 | "repo": "nixpkgs", 151 | "rev": "adaa24fbf46737f3f1b5497bf64bae750f82942e", 152 | "type": "github" 153 | }, 154 | "original": { 155 | "owner": "NixOS", 156 | "ref": "nixos-unstable", 157 | "repo": "nixpkgs", 158 | "type": "github" 159 | } 160 | }, 161 | "nixpkgs-23-11": { 162 | "locked": { 163 | "lastModified": 1717159533, 164 | "narHash": "sha256-oamiKNfr2MS6yH64rUn99mIZjc45nGJlj9eGth/3Xuw=", 165 | "owner": "NixOS", 166 | "repo": "nixpkgs", 167 | "rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446", 168 | "type": "github" 169 | }, 170 | "original": { 171 | "owner": "NixOS", 172 | "repo": "nixpkgs", 173 | "rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446", 174 | "type": "github" 175 | } 176 | }, 177 | "nixpkgs-regression": { 178 | "locked": { 179 | "lastModified": 1643052045, 180 | "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", 181 | "owner": "NixOS", 182 | "repo": "nixpkgs", 183 | "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", 184 | "type": "github" 185 | }, 186 | "original": { 187 | "owner": "NixOS", 188 | "repo": "nixpkgs", 189 | "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", 190 | "type": "github" 191 | } 192 | }, 193 | "nixpkgs_2": { 194 | "locked": { 195 | "lastModified": 1748370509, 196 | "narHash": "sha256-QlL8slIgc16W5UaI3w7xHQEP+Qmv/6vSNTpoZrrSlbk=", 197 | "owner": "nixos", 198 | "repo": "nixpkgs", 199 | "rev": "4faa5f5321320e49a78ae7848582f684d64783e9", 200 | "type": "github" 201 | }, 202 | "original": { 203 | "owner": "nixos", 204 | "ref": "nixos-unstable", 205 | "repo": "nixpkgs", 206 | "type": "github" 207 | } 208 | }, 209 | "nocargo": { 210 | "inputs": { 211 | "flake-utils": "flake-utils_2", 212 | "nix-filter": "nix-filter", 213 | "nixpkgs": [ 214 | "nixpkgs" 215 | ], 216 | "registry-crates-io": [ 217 | "registry-crates-io" 218 | ] 219 | }, 220 | "locked": { 221 | "lastModified": 1748486175, 222 | "narHash": "sha256-P86B46vyp11GDcm74heD4yc/U5CWdZHQsdNSf9NS6nQ=", 223 | "owner": "o-santi", 224 | "repo": "nocargo", 225 | "rev": "9a12309550e40affcc2f170e09c08ece63d54a90", 226 | "type": "github" 227 | }, 228 | "original": { 229 | "owner": "o-santi", 230 | "repo": "nocargo", 231 | "type": "github" 232 | } 233 | }, 234 | "registry-crates-io": { 235 | "flake": false, 236 | "locked": { 237 | "lastModified": 1737733755, 238 | "narHash": "sha256-g15m8IfFGWO4MoFmzAj9nFDjorTM9N1viP/83mCWNbA=", 239 | "owner": "rust-lang", 240 | "repo": "crates.io-index", 241 | "rev": "9dd148c9b916aa1aaf36d2b395b9428a385c0767", 242 | "type": "github" 243 | }, 244 | "original": { 245 | "owner": "rust-lang", 246 | "repo": "crates.io-index", 247 | "type": "github" 248 | } 249 | }, 250 | "root": { 251 | "inputs": { 252 | "flake-utils": "flake-utils", 253 | "nix": "nix", 254 | "nixpkgs": "nixpkgs_2", 255 | "nocargo": "nocargo", 256 | "registry-crates-io": "registry-crates-io", 257 | "rust-overlay": "rust-overlay" 258 | } 259 | }, 260 | "rust-overlay": { 261 | "inputs": { 262 | "nixpkgs": [ 263 | "nixpkgs" 264 | ] 265 | }, 266 | "locked": { 267 | "lastModified": 1733711706, 268 | "narHash": "sha256-uDfJ/TrLLqrtoNzfPODDOVyZ+JWsJfd5T1r7xuE6h6g=", 269 | "owner": "oxalica", 270 | "repo": "rust-overlay", 271 | "rev": "4eb3f096e14431bd0ab4cca039f9c9d77331cbfc", 272 | "type": "github" 273 | }, 274 | "original": { 275 | "owner": "oxalica", 276 | "repo": "rust-overlay", 277 | "type": "github" 278 | } 279 | }, 280 | "systems": { 281 | "locked": { 282 | "lastModified": 1681028828, 283 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 284 | "owner": "nix-systems", 285 | "repo": "default", 286 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 287 | "type": "github" 288 | }, 289 | "original": { 290 | "owner": "nix-systems", 291 | "repo": "default", 292 | "type": "github" 293 | } 294 | }, 295 | "systems_2": { 296 | "locked": { 297 | "lastModified": 1681028828, 298 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 299 | "owner": "nix-systems", 300 | "repo": "default", 301 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 302 | "type": "github" 303 | }, 304 | "original": { 305 | "owner": "nix-systems", 306 | "repo": "default", 307 | "type": "github" 308 | } 309 | } 310 | }, 311 | "root": "root", 312 | "version": 7 313 | } 314 | -------------------------------------------------------------------------------- /nix-for-rust/src/derivation.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{HashMap, HashSet}; 2 | use std::path::{Path, PathBuf}; 3 | use crate::eval::NixEvalState; 4 | use crate::store::{NixStore, NixStorePath}; 5 | use crate::term::{CollectToNix, NixAttrSet, NixList, NixResult, NixTerm, ToNix}; 6 | use anyhow::Result; 7 | use nom::bytes::complete::{escaped_transform, tag}; 8 | use nom::combinator::{fail, opt, value}; 9 | use nom::error::VerboseError; 10 | use nom::multi::separated_list0; 11 | use nom::branch::alt; 12 | use nom::character::complete::{char, none_of}; 13 | use nom::sequence::delimited; 14 | use nom::{Finish, IResult, Parser}; 15 | 16 | type ParseRes<'s, T> = IResult<&'s str, T, VerboseError<&'s str>>; 17 | 18 | #[derive(Debug)] 19 | pub enum HashAlgorithm { 20 | Md5, 21 | Sha1, 22 | Sha256, 23 | Sha512 24 | } 25 | 26 | impl HashAlgorithm { 27 | fn parse(s: &str) -> Option { 28 | match s { 29 | "md5" => Some(HashAlgorithm::Md5), 30 | "sha1" => Some(HashAlgorithm::Sha1), 31 | "sha256" => Some(HashAlgorithm::Sha256), 32 | "sha512" => Some(HashAlgorithm::Sha512), 33 | _ => None 34 | } 35 | } 36 | } 37 | 38 | #[derive(Debug)] 39 | pub enum ContentAddressedMethod { 40 | NixArchive, 41 | Git, 42 | Text, 43 | Flat 44 | } 45 | 46 | impl ContentAddressedMethod { 47 | fn parse(s: &str) -> (&str, Self) { 48 | if let Some(s) = s.strip_prefix("r:") { 49 | (s, ContentAddressedMethod::NixArchive) 50 | } else if let Some(s) = s.strip_prefix("git:") { 51 | (s, ContentAddressedMethod::Git) 52 | } else if let Some(s) = s.strip_prefix("text:") { 53 | (s, ContentAddressedMethod::Text) 54 | } else { 55 | (s, ContentAddressedMethod::Flat) 56 | } 57 | } 58 | } 59 | 60 | #[derive(Debug)] 61 | pub enum DerivationOutput<'store> { 62 | Deferred, 63 | InputAddressed { 64 | path: NixStorePath<'store>, 65 | }, 66 | Impure { 67 | method: ContentAddressedMethod, 68 | hash_algo: HashAlgorithm 69 | }, 70 | CAFixed { 71 | method: ContentAddressedMethod, 72 | hash_algo: HashAlgorithm, 73 | hash: String 74 | }, 75 | CAFloating { 76 | method: ContentAddressedMethod, 77 | hash_algo: HashAlgorithm 78 | } 79 | } 80 | 81 | #[derive(Debug)] 82 | pub enum InputDrv { 83 | Paths(HashSet), 84 | Map(HashMap), 85 | } 86 | 87 | impl InputDrv { 88 | fn parse<'src>(input: &'src str, version: DerivationVersion) -> ParseRes<'src, Self> { 89 | match version { 90 | DerivationVersion::Traditional => { 91 | let (input, strs) = string_set(input)?; 92 | Ok((input, InputDrv::Paths(strs))) 93 | }, 94 | DerivationVersion::Dynamic => todo!(), 95 | } 96 | } 97 | } 98 | 99 | #[derive(Debug, Clone, Copy)] 100 | pub enum DerivationVersion { 101 | Traditional, 102 | Dynamic 103 | } 104 | 105 | #[derive(Debug)] 106 | pub struct Derivation<'store> { 107 | pub version: DerivationVersion, 108 | pub name: String, 109 | pub outputs: HashMap>, 110 | pub input_srcs: HashSet, 111 | pub input_drvs: HashMap, 112 | pub platform: String, 113 | pub builder: PathBuf, 114 | pub args: Vec, 115 | pub env: HashMap 116 | } 117 | 118 | impl<'store> DerivationOutput<'store> { 119 | fn new(store: &'store NixStore, path: &str, hash_algo: &str, hash: &str) -> Result { 120 | if !hash_algo.is_empty() { 121 | let (rest, method) = ContentAddressedMethod::parse(hash_algo); 122 | let Some(hash_algo) = HashAlgorithm::parse(rest) else { 123 | anyhow::bail!("Unrecognized hash algorithm"); 124 | }; 125 | if hash == "impure" { 126 | if !path.is_empty() { 127 | anyhow::bail!("Impure derivation output should not specify output path"); 128 | } 129 | return Ok(DerivationOutput::Impure { method, hash_algo }); 130 | } else if !hash.is_empty() { 131 | // TODO: validate path 132 | return Ok(DerivationOutput::CAFixed { method, hash_algo, hash: hash.to_string()}); 133 | } else { 134 | if !path.is_empty() { 135 | anyhow::bail!("Impure derivation output should not specify output path"); 136 | } 137 | return Ok(DerivationOutput::CAFloating { method, hash_algo }); 138 | } 139 | } else { 140 | if path.is_empty() { 141 | return Ok(DerivationOutput::Deferred); 142 | } 143 | return Ok(DerivationOutput::InputAddressed { path: store.parse_path(path)? }); 144 | } 145 | } 146 | } 147 | 148 | fn string_set(input: &str) -> ParseRes> { 149 | list(string)(input).map(|(rest, s)| (rest, s.into_iter().map(|s| s.to_string()).collect())) 150 | } 151 | 152 | fn parse_input(input: &str, version: DerivationVersion) -> ParseRes<(String, InputDrv)> { 153 | let (input, _) = tag("(")(input)?; 154 | let (input, name) = string(input)?; 155 | let (input, _) = tag(",")(input)?; 156 | let (input, input_drv) = InputDrv::parse(input, version)?; 157 | let (input, _) = tag(")")(input)?; 158 | Ok((input, (name.to_string(), input_drv))) 159 | } 160 | 161 | fn string<'src>(input: &'src str) -> ParseRes<'src, String> { 162 | let (input, _) = char('"')(input)?; 163 | let (input, s) = opt(escaped_transform(none_of("\"\\"), '\\', alt(( 164 | value("\\", tag("\\")), 165 | value("\"", tag("\"")), 166 | value("\n", tag("n")), 167 | value("\r", tag("r")), 168 | value("\t", tag("t")), 169 | ))))(input)?; 170 | let (input, _) = char('"')(input)?; 171 | Ok((input, s.unwrap_or("".to_string()))) 172 | } 173 | 174 | fn parse_drv_output<'store, 'src>(store: &'store NixStore, input: &'src str) -> ParseRes<'src, (String, DerivationOutput<'store>)> { 175 | let (input, _) = tag("(")(input)?; 176 | let (input, name) = string(input)?; 177 | let (input, _) = tag(",")(input)?; 178 | let (input, path) = string(input)?; 179 | let (input, _) = tag(",")(input)?; 180 | let (input, hash_algo) = string(input)?; 181 | let (input, _) = tag(",")(input)?; 182 | let (input, hash) = string(input)?; 183 | let (input, _) = tag(")")(input)?; 184 | let Ok(drv) = DerivationOutput::new(store, &path, &hash_algo, &hash) else { 185 | return fail(input); 186 | }; 187 | Ok((input, (name.to_string(), drv))) 188 | } 189 | 190 | fn list<'src, O, P: Parser<&'src str, O, VerboseError<&'src str>>>(parser: P) -> impl FnMut(&'src str) -> ParseRes<'src, Vec> { 191 | delimited(char('['), separated_list0(char(','), parser), char(']')) 192 | } 193 | 194 | fn parse_pair(input: &str) -> ParseRes<(String, String)> { 195 | let (input, _) = tag("(")(input)?; 196 | let (input, name) = string(input)?; 197 | let (input, _) = tag(",")(input)?; 198 | let (input, value) = string(input)?; 199 | let (input, _) = tag(")")(input)?; 200 | Ok((input, (name.to_string(), value.to_string()))) 201 | } 202 | 203 | fn parse_version(input: &str) -> ParseRes { 204 | let traditional = value(DerivationVersion::Traditional, tag("Derive(")); 205 | let dynamic = |input| { 206 | let (input, _) = tag("DrvWithVersion(")(input)?; 207 | let (input, v) = string(input)?; 208 | if v != "xp-dyn-drv" { 209 | return fail(input); 210 | } 211 | return Ok((input, DerivationVersion::Dynamic)) 212 | }; 213 | alt((traditional, dynamic))(input) 214 | } 215 | 216 | fn parse_derivation<'store, 'src>(store: &'store NixStore, name: String, input: &'src str) -> ParseRes<'src, Derivation<'store>> { 217 | let (input, version) = parse_version(input)?; 218 | let (input, outputs) = list(|i| parse_drv_output(store, i))(input)?; 219 | let (input, _) = tag(",")(input)?; 220 | let (input, input_drvs) = list(move |i| parse_input(i, version))(input)?; 221 | let (input, _) = tag(",")(input)?; 222 | let (input, input_srcs) = string_set(input)?; 223 | let (input, _) = tag(",")(input)?; 224 | let (input, platform) = string(input)?; 225 | let (input, _) = tag(",")(input)?; 226 | let (input, builder) = string(input)?; 227 | let (input, _) = tag(",")(input)?; 228 | let (input, args) = list(string)(input)?; 229 | let (input, _) = tag(",")(input)?; 230 | let (input, env) = list(parse_pair)(input)?; 231 | let (input, _) = tag(")")(input)?; 232 | let drv = Derivation { 233 | version, 234 | name, 235 | outputs: outputs.into_iter().collect(), 236 | input_srcs: input_srcs.into_iter().collect(), 237 | input_drvs: input_drvs.into_iter().collect(), 238 | platform: platform.to_string(), 239 | builder: Path::new(&builder).to_path_buf(), 240 | args, 241 | env: env.into_iter().collect(), 242 | }; 243 | Ok((input, drv)) 244 | } 245 | 246 | impl NixStore { 247 | pub fn parse_derivation(&self, drv_path: &str) -> Result { 248 | let drv_path = self.parse_path(drv_path)?; 249 | let content = std::fs::read_to_string(&drv_path.path)?; 250 | let drv_name = drv_path.name()?; 251 | let name = drv_name 252 | .strip_suffix(".drv") 253 | .ok_or_else(|| anyhow::format_err!("Path is not derivation."))?; 254 | let (_, drv) = parse_derivation(self, name.to_string(), &content) 255 | .finish() 256 | .map_err(|e| anyhow::format_err!("{}", nom::error::convert_error(content.as_str(), e)))?; 257 | Ok(drv) 258 | } 259 | } 260 | 261 | impl<'store, 'state> ToNix<'state> for Derivation<'store> { 262 | fn to_nix(self, eval_state: &'state NixEvalState) -> NixResult> { 263 | let mut args: HashMap<&str, NixTerm> = self.env 264 | .iter() 265 | .map(|(k, v)| (k.as_str(), v.into())) 266 | .collect(); 267 | args.insert("name", self.name.into()); 268 | args.insert("builder", self.builder.into()); 269 | args.insert("system", self.platform.into()); 270 | args.insert("args", self.args.into_iter().collect_to_nix::(eval_state)?.into()); 271 | args.insert("outputs", self.outputs.keys().collect_to_nix::(eval_state)?.into()); 272 | let derivation = eval_state.eval_string("builtins.derivation", std::env::current_dir() 273 | .expect("Could not get cwd")) 274 | .expect("builtins.derivation should never fail"); 275 | derivation.call_with(args.into_iter().collect_to_nix::(eval_state)?) 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /nix-for-rust/src/term.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_upper_case_globals)] 2 | use std::collections::HashMap; 3 | use std::ffi::{c_char, c_uint, CStr, CString}; 4 | use std::ptr::NonNull; 5 | use std::path::PathBuf; 6 | use crate::bindings::{bindings_builder_free, bindings_builder_insert, get_attr_byidx, get_attr_byname, get_attr_name_byidx, get_attrs_size, get_bool, get_float, get_int, get_list_byidx, get_list_size, get_path_string, get_string, get_type, init_bool, init_float, init_int, init_null, init_path_string, init_string, list_builder_insert, make_attrs, make_bindings_builder, make_list, make_list_builder, realised_string_get_buffer_size, realised_string_get_buffer_start, realised_string_get_store_path, realised_string_get_store_path_count, string_realise, value_call, value_force, ValueType}; 7 | use crate::error::NixError; 8 | use crate::eval::{NixEvalState, RawValue}; 9 | use crate::store::{NixContext, NixStorePath}; 10 | use crate::utils::{callback_get_result_string, callback_get_result_string_data}; 11 | use thiserror::Error; 12 | 13 | /// Type of hashmaps that can be represented as a nix attrset 14 | pub type AttrSet<'str, 'state> = std::collections::HashMap<&'str str, NixTerm<'state>>; 15 | 16 | /// An error that might happen when evaluating nix expressions. 17 | #[derive(Debug, Error)] 18 | pub enum NixEvalError { 19 | #[error("{0}")] 20 | RuntimeError(NixError), 21 | #[error("Type error, expected '{expected}' but got '{got}'")] 22 | TypeError { expected: String, got: String }, 23 | #[error("Cannot build non-derivation")] 24 | NotADerivation, 25 | #[error("Index out of bounds!")] 26 | IndexOutOfBounds, 27 | #[error("Nix returned invalid string")] 28 | InvalidString, 29 | #[error("Invalid path '{0}'")] 30 | InvalidPath(String), 31 | #[error("Empty attribute path")] 32 | AttrPathEmpty, 33 | } 34 | 35 | pub type NixResult = Result; 36 | 37 | 38 | /// Wrapper around a pointer to nix attribute set. 39 | #[derive(Clone)] 40 | pub struct NixAttrSet<'state>(pub(crate) RawValue<'state>); 41 | /// Wrapper around a pointer to nix list. 42 | #[derive(Clone)] 43 | pub struct NixList<'state>(pub(crate) RawValue<'state>); 44 | /// Wrapper around a pointer to nix function. 45 | #[derive(Clone)] 46 | pub struct NixFunction<'state>(pub(crate) RawValue<'state>); 47 | /// Wrapper around a pointer to nix thunk 48 | #[derive(Clone)] 49 | pub struct NixThunk<'state>(pub(crate) RawValue<'state>); 50 | 51 | pub struct NixRealisedString<'store> { 52 | pub string: String, 53 | pub paths: Vec> 54 | } 55 | 56 | /// A nix term represented as a rust value. 57 | pub enum NixTerm<'state> { 58 | Null, 59 | Thunk(NixThunk<'state>), 60 | Int(i64), 61 | Float(f64), 62 | Bool(bool), 63 | List(NixList<'state>), 64 | Path(PathBuf), 65 | AttrSet(NixAttrSet<'state>), 66 | String(String), 67 | External(RawValue<'state>), 68 | Function(NixFunction<'state>) 69 | } 70 | 71 | /// Conversion trait between rust objects and nix values. 72 | pub trait ToNix<'state> { 73 | fn to_nix(self, eval_state: &'state NixEvalState) -> NixResult>; 74 | } 75 | 76 | pub trait FromIterToNix<'state, I>: Sized { 77 | fn from_iter_to_nix(iter: T, state: &'state NixEvalState) -> NixResult 78 | where T: IntoIterator + ExactSizeIterator; 79 | } 80 | 81 | pub trait CollectToNix<'state, T>: Iterator + Sized + ExactSizeIterator { 82 | fn collect_to_nix>(self, state: &'state NixEvalState) -> NixResult { 83 | O::from_iter_to_nix(self, state) 84 | } 85 | } 86 | 87 | impl<'state, T, I: Iterator + Sized + ExactSizeIterator> CollectToNix<'state, T> for I {} 88 | 89 | /// Trait to print a nix term, which may throw errors during evaluation time. 90 | pub trait Repr { 91 | fn repr_rec(&self, s: &mut String) -> NixResult<()>; 92 | /// Returns a string with the objects representation, or an error that happened during evaluation 93 | fn repr(&self) -> NixResult { 94 | let mut buf = String::new(); 95 | self.repr_rec(&mut buf)?; 96 | Ok(buf) 97 | } 98 | } 99 | 100 | impl<'state, N: Into>> ToNix<'state> for N { 101 | fn to_nix(self, _eval_state: &'state NixEvalState) -> NixResult> { 102 | Ok(self.into()) 103 | } 104 | } 105 | 106 | impl<'state> ToNix<'state> for RawValue<'state> { 107 | fn to_nix(self, _eval_state: &'state NixEvalState) -> NixResult> { 108 | let context = NixContext::default(); 109 | let ctx = context.ptr(); 110 | let value = self.value.as_ptr(); 111 | let value_type = unsafe { get_type(ctx, value) }; 112 | let res = match value_type { 113 | ValueType::NIX_TYPE_NULL => NixTerm::Null, 114 | ValueType::NIX_TYPE_INT => { 115 | let v = unsafe { get_int(ctx, value) }; 116 | NixTerm::Int(v) 117 | }, 118 | ValueType::NIX_TYPE_BOOL => { 119 | let b = unsafe { get_bool(ctx, value) }; 120 | NixTerm::Bool(b) 121 | }, 122 | ValueType::NIX_TYPE_FLOAT => { 123 | let f = unsafe { get_float(ctx, value) }; 124 | NixTerm::Float(f) 125 | }, 126 | ValueType::NIX_TYPE_STRING => { 127 | let mut raw_buffer: anyhow::Result = Err(anyhow::format_err!("Nix C API didn't return a string.")); 128 | unsafe { 129 | get_string(ctx, value, Some(callback_get_result_string), callback_get_result_string_data(&mut raw_buffer)) 130 | }; 131 | NixTerm::String(raw_buffer.map_err(|_| NixEvalError::InvalidString)?) 132 | }, 133 | ValueType::NIX_TYPE_PATH => { 134 | let path = unsafe { get_path_string(ctx, value) }; 135 | let path = unsafe { CStr::from_ptr(path) }; 136 | let path = path.to_str().map_err(|_| NixEvalError::InvalidString)?; 137 | let path = PathBuf::from(path); 138 | NixTerm::Path(path) 139 | }, 140 | ValueType::NIX_TYPE_LIST => NixTerm::List(NixList(self)), 141 | ValueType::NIX_TYPE_ATTRS => NixTerm::AttrSet(NixAttrSet(self)), 142 | ValueType::NIX_TYPE_FUNCTION => NixTerm::Function(NixFunction(self)), 143 | ValueType::NIX_TYPE_THUNK => NixTerm::Thunk(NixThunk(self)), 144 | ValueType::NIX_TYPE_EXTERNAL => todo!("Cannot handle external values yet"), 145 | _ => panic!("Unknown value type"), 146 | }; 147 | context.check_call()?; 148 | Ok(res) 149 | } 150 | } 151 | 152 | /// Iterator over elements in a nix list 153 | pub struct NixListIterator<'state, 'val: 'state> { 154 | pub len: u32, 155 | pub(crate) val: &'val NixList<'state>, 156 | pub(crate) idx: u32 157 | } 158 | 159 | /// Iterator over items in a nix attribute set 160 | pub struct NixItemsIterator<'state, 'val: 'state> { 161 | pub len: u32, 162 | pub(crate) val: &'val NixAttrSet<'state>, 163 | pub(crate) idx: u32 164 | } 165 | 166 | /// Iterator over keys in a nix attribute set 167 | pub struct NixNamesIterator<'state, 'val: 'state> { 168 | pub len: u32, 169 | pub(crate) val: &'val NixAttrSet<'state>, 170 | pub(crate) idx: u32 171 | } 172 | 173 | impl<'state> Repr for NixAttrSet<'state> { 174 | fn repr_rec(&self, s: &mut String) -> NixResult<()> { 175 | s.push('{'); 176 | for (key, val) in self.items()? { 177 | let val = val?; 178 | s.push(' '); 179 | s.push_str(&key); 180 | s.push_str(" = "); 181 | if let NixTerm::List(_) = val { 182 | s.push_str(" [ ... ]"); 183 | } else if let NixTerm::AttrSet(_) = val { 184 | s.push_str(" { ... }"); 185 | } else { 186 | val.repr_rec(s)?; 187 | }; 188 | s.push(';'); 189 | } 190 | s.push_str(" }"); 191 | Ok(()) 192 | } 193 | } 194 | 195 | impl<'state> NixAttrSet<'state> { 196 | 197 | /// Tries to build an attribute set as if it was a derivation. 198 | /// 199 | /// Throws [`NotADerivation`][NixEvalError] if the attrset is not a derivation 200 | pub fn realise(&self) -> anyhow::Result> { 201 | let ctx = self.0._state.store.ctx.ptr(); 202 | let realised_string = unsafe { 203 | string_realise(ctx, self.0._state.state_ptr(), self.0.value.as_ptr(), false) 204 | }; 205 | let path_count = unsafe { 206 | realised_string_get_store_path_count(realised_string) 207 | }; 208 | let store = &self.0._state.store; 209 | let paths: Vec = (0..path_count) 210 | .map(move |i| { 211 | let path = unsafe { 212 | realised_string_get_store_path(realised_string, i) 213 | }; 214 | NixStorePath::from_ptr(store, path as *mut _) 215 | }) 216 | .collect::>()?; 217 | let string = unsafe { 218 | let start = realised_string_get_buffer_start(realised_string) as *const u8; 219 | let size = realised_string_get_buffer_size(realised_string); 220 | let slice = std::slice::from_raw_parts(start, size); 221 | String::from_utf8(slice.to_vec()) 222 | .map_err(|e| anyhow::format_err!("Nix string is not valid UTF-8: {}", e))? 223 | }; 224 | Ok(NixRealisedString { string, paths }) 225 | } 226 | 227 | /// Gets an attribute from the underlying attribute set. 228 | /// 229 | /// Throws [`RuntimeError(KeyError)`][NixError] when the key doesn't exist. 230 | pub fn get(&self, name: &str) -> NixResult> { 231 | let ctx = &self.0._state.store.ctx; 232 | let state = &self.0._state; 233 | let name = CString::new(name).map_err(|_| NixEvalError::InvalidString)?; 234 | let val = unsafe { 235 | get_attr_byname(ctx.ptr(), self.0.value.as_ptr(), state.state_ptr(), name.as_ptr()) 236 | }; 237 | ctx.check_call()?; 238 | let value = NonNull::new(val).expect("get_attr_by_name returned null"); 239 | let rawvalue = RawValue { 240 | value, 241 | _state: state 242 | }; 243 | rawvalue.to_nix(&self.0._state) 244 | } 245 | 246 | /// How many elements there are in the attribute set. 247 | pub fn len(&self) -> NixResult { 248 | let len = unsafe { get_attrs_size(self.0._state.store.ctx.ptr(), self.0.value.as_ptr()) }; 249 | self.0._state.store.ctx.check_call()?; 250 | Ok(len) 251 | } 252 | 253 | /// Whether it's empty or not. 254 | pub fn is_empty(&self) -> NixResult { 255 | Ok(self.len()? == 0) 256 | } 257 | 258 | /// Returns an iterator over the keys of the attribute set. 259 | pub fn names(&self) -> NixResult { 260 | let iterator = NixNamesIterator { 261 | val: &self, len: self.len()?, idx: 0 262 | }; 263 | Ok(iterator) 264 | } 265 | 266 | /// Returns an iterator over the pairs `(String, NixTerm)` of the attribute set. 267 | pub fn items(&self) -> NixResult { 268 | let iterator = NixItemsIterator { 269 | val: &self, len: self.len()?, idx: 0 270 | }; 271 | Ok(iterator) 272 | } 273 | } 274 | 275 | impl<'state> NixThunk<'state> { 276 | /// Forces the evaluation of the thunk and resolves it into 277 | /// a non-thunk term. 278 | pub fn force(self) -> NixResult> { 279 | let rawvalue = &self.0; 280 | let context = rawvalue._state.store.ctx.ptr(); 281 | let state = rawvalue._state.state_ptr(); 282 | let value = rawvalue.value.as_ptr(); 283 | unsafe { 284 | value_force(context, state, value) 285 | }; 286 | rawvalue._state.store.ctx.check_call()?; 287 | rawvalue.clone().to_nix(&rawvalue._state) 288 | } 289 | } 290 | 291 | impl<'state> NixFunction<'state> { 292 | /// Calls the nix function with the argument converted to nix. 293 | pub fn call_with>(&self, arg: T) -> NixResult> { 294 | let state = self.0._state.state_ptr(); 295 | let arg = arg.to_nix(&self.0._state)?.to_raw_value(&self.0._state); 296 | let ret = RawValue::empty(self.0._state); 297 | let ctx = NixContext::default(); 298 | unsafe { 299 | value_call(ctx.ptr(), state, self.0.value.as_ptr(), arg.value.as_ptr(), ret.value.as_ptr()); 300 | } 301 | ctx.check_call()?; 302 | ret.to_nix(&self.0._state) 303 | } 304 | } 305 | 306 | impl<'state> NixList<'state> { 307 | 308 | /// How many elements are there in the list. 309 | pub fn len(&self) -> NixResult { 310 | let len = unsafe { get_list_size(self.0._state.store.ctx.ptr(), self.0.value.as_ptr()) }; 311 | self.0._state.store.ctx.check_call()?; 312 | Ok(len) 313 | } 314 | 315 | /// Is the list empty? 316 | pub fn is_empty(&self) -> NixResult { 317 | Ok(self.len()? == 0) 318 | } 319 | 320 | /// Returns the iterator over the elements in a list 321 | pub fn iter(&self) -> NixResult { 322 | let iterator = NixListIterator { 323 | val: &self, len: self.len()?, idx: 0 324 | }; 325 | Ok(iterator) 326 | } 327 | 328 | /// Returns the element at idx `idx` or throws an `IndexOutOfBounds` error. 329 | pub fn get_idx(&self, idx: u32) -> NixResult> { 330 | let raw = &self.0; 331 | let size = self.len()?; 332 | if idx > size -1 { 333 | return Err(NixEvalError::IndexOutOfBounds) 334 | } 335 | let elem = unsafe { get_list_byidx(raw._state.store.ctx.ptr(), raw.value.as_ptr(), raw._state.state_ptr(), idx as c_uint) }; 336 | let value = NonNull::new(elem).expect("get_list_byidx returned null"); 337 | let rawvalue = RawValue { 338 | _state: raw._state, 339 | value 340 | }; 341 | rawvalue.to_nix(&raw._state) 342 | } 343 | } 344 | 345 | impl<'state> Repr for NixList<'state> { 346 | fn repr_rec(&self, s: &mut String) -> NixResult<()> { 347 | s.push('['); 348 | for t in self.iter()? { 349 | let t = t?; 350 | s.push(' '); 351 | if let NixTerm::List(_) = t { 352 | s.push_str("[ ... ]"); 353 | } else if let NixTerm::AttrSet(_) = t { 354 | s.push_str("{ ... }"); 355 | } else { 356 | t.repr_rec(s)?; 357 | } 358 | } 359 | s.push_str(" ]"); 360 | Ok(()) 361 | } 362 | } 363 | 364 | impl<'state> NixTerm<'state> { 365 | 366 | /// Builds the term if the term is an attribute set, otherwise type error. 367 | pub fn build(&self) -> anyhow::Result { 368 | if let NixTerm::AttrSet(attrset) = self { 369 | attrset.realise() 370 | } else { 371 | let err = NixEvalError::TypeError { expected: "attrset".to_string(), got: self.get_typename() }; 372 | Err(err.into()) 373 | } 374 | } 375 | 376 | /// Returns the name of the type of the term. 377 | pub fn get_typename(&self) -> String { 378 | match self { 379 | NixTerm::Null => "null", 380 | NixTerm::Thunk(_) => "thunk", 381 | NixTerm::Int(_) => "int", 382 | NixTerm::Float(_) => "float", 383 | NixTerm::Bool(_) => "bool", 384 | NixTerm::List(_) => "list", 385 | NixTerm::Path(_) => "path", 386 | NixTerm::AttrSet(_) => "attrset", 387 | NixTerm::String(_) => "string", 388 | NixTerm::External(_) => "external", 389 | NixTerm::Function(_) => "function" 390 | }.to_string() 391 | } 392 | 393 | /// Returns the iterator over names if the element is an attrset, otherwise type error. 394 | pub fn names<'slf>(&'slf self) -> NixResult> { 395 | if let NixTerm::AttrSet(attrset) = self { 396 | attrset.names() 397 | } else { 398 | Err(NixEvalError::TypeError { expected: "attrset".into(), got: self.get_typename() }) 399 | } 400 | } 401 | 402 | /// Returns the iterator over items if the element is an attrset, otherwise type error. 403 | pub fn items<'slf>(&'slf self) -> NixResult> { 404 | if let NixTerm::AttrSet(attrset) = self { 405 | attrset.items() 406 | } else { 407 | Err(NixEvalError::TypeError { expected: "attrset".into(), got: self.get_typename() }) 408 | } 409 | } 410 | 411 | pub fn iter<'slf>(&'slf self) -> NixResult> { 412 | if let NixTerm::List(list) = self { 413 | list.iter() 414 | } 415 | else { 416 | Err(NixEvalError::TypeError { expected: "list".into(), got: self.get_typename() }) 417 | } 418 | } 419 | 420 | pub fn to_raw_value(self, _state: &'state NixEvalState) -> RawValue<'state> { 421 | let ctx = _state.store.ctx.ptr(); 422 | let state = _state.state_ptr(); 423 | let mut rawval = RawValue::empty(_state); 424 | let val_ptr = rawval.value.as_ptr(); 425 | match self { 426 | NixTerm::External(raw) => { rawval = raw; } 427 | NixTerm::Thunk(thunk) => { rawval = thunk.0; } 428 | NixTerm::List(list) => { rawval = list.0; } 429 | NixTerm::AttrSet(attrset) => { rawval = attrset.0; } 430 | NixTerm::Function(func) => { rawval = func.0; }, 431 | NixTerm::Null => unsafe { 432 | init_null(ctx, val_ptr); 433 | } 434 | NixTerm::Int(i) => unsafe { 435 | init_int(ctx, val_ptr, i); 436 | } 437 | NixTerm::Float(f) => unsafe { 438 | init_float(ctx, val_ptr, f); 439 | } 440 | NixTerm::Bool(b) => unsafe { 441 | init_bool(ctx, val_ptr, b); 442 | } 443 | NixTerm::Path(p) => { 444 | let string = p.to_str().expect("path is not a valid string"); 445 | let c_str = CString::new(string).expect("path is not a valid C String"); 446 | unsafe { 447 | init_path_string(ctx, state, val_ptr, c_str.as_ptr()); 448 | } 449 | }, 450 | NixTerm::String(s) => { 451 | let c_str = CString::new(s.to_owned()).expect("path is not a valid C String"); 452 | unsafe { 453 | init_string(ctx, val_ptr, c_str.as_ptr()); 454 | } 455 | }, 456 | }; 457 | _state.store.ctx.check_call().expect("error transforming to raw value"); 458 | rawval 459 | } 460 | 461 | pub fn call_with>(&self, arg: T) -> NixResult> { 462 | if let NixTerm::Function(func) = self { 463 | func.call_with(arg) 464 | } else { 465 | Err(NixEvalError::TypeError { expected: "function".into(), got: self.get_typename() }) 466 | } 467 | } 468 | 469 | pub fn get(&self, name: &str) -> NixResult> { 470 | if let NixTerm::AttrSet(attrset) = self { 471 | attrset.get(name) 472 | } else { 473 | Err(NixEvalError::TypeError { expected: "attrset".into(), got: self.get_typename() }) 474 | } 475 | } 476 | 477 | pub fn get_attrs, P: IntoIterator>(&self, path: P) -> NixResult> { 478 | let mut path = path.into_iter(); 479 | if let Some(p) = path.next() { 480 | let mut attrset = self.get(p.as_ref())?; 481 | for attr in path { 482 | attrset = attrset.get(attr.as_ref())?; 483 | } 484 | Ok(attrset) 485 | } else { 486 | Err(NixEvalError::AttrPathEmpty) 487 | } 488 | } 489 | 490 | 491 | pub fn as_bool(&self) -> NixResult { 492 | let NixTerm::Bool(b) = self else { 493 | return Err(NixEvalError::TypeError { expected: "bool".into(), got: self.get_typename() }); 494 | }; 495 | Ok(*b) 496 | } 497 | 498 | pub fn as_int(&self) -> NixResult { 499 | let NixTerm::Int(i) = self else { 500 | return Err(NixEvalError::TypeError { expected: "float".into(), got: self.get_typename() }); 501 | }; 502 | Ok(*i) 503 | } 504 | 505 | pub fn as_float(&self) -> NixResult { 506 | let NixTerm::Float(f) = self else { 507 | return Err(NixEvalError::TypeError { expected: "float".into(), got: self.get_typename() }); 508 | }; 509 | Ok(*f) 510 | } 511 | 512 | pub fn as_string(&self) -> NixResult { 513 | let NixTerm::String(s) = self else { 514 | return Err(NixEvalError::TypeError { expected: "string".into(), got: self.get_typename() }); 515 | }; 516 | Ok(s.to_string()) 517 | } 518 | 519 | pub fn as_list<'slf: 'state>(&'slf self) -> NixResult>> { 520 | self.iter()?.collect::>() 521 | } 522 | 523 | pub fn as_hashmap<'slf: 'state>(&'slf self) -> NixResult>>> { 524 | Ok(self.items()?.collect()) 525 | } 526 | 527 | pub fn as_path(&self) -> NixResult<&PathBuf> { 528 | let NixTerm::Path(p) = &self else { 529 | return Err(NixEvalError::TypeError { expected: "path".into(), got: self.get_typename() }); 530 | }; 531 | Ok(p) 532 | } 533 | 534 | } 535 | 536 | impl<'state> Repr for NixTerm<'state> { 537 | fn repr_rec(&self, s: &mut String) -> NixResult<()> { 538 | match self { 539 | NixTerm::Null => s.push_str("null"), 540 | NixTerm::Thunk(_) => s.push_str("<...>"), 541 | NixTerm::Int(i) => s.push_str(&i.to_string()), 542 | NixTerm::Float(float) => s.push_str(&float.to_string()), 543 | NixTerm::Bool(b) => s.push_str(&b.to_string()), 544 | NixTerm::Path(path) => s.push_str(path.to_str().expect("Cannot convert path to string")), 545 | NixTerm::String(str) => s.push_str(str), 546 | NixTerm::List(list) => list.repr_rec(s)?, 547 | NixTerm::AttrSet(attrset) => attrset.repr_rec(s)?, 548 | NixTerm::External(_) => todo!(), 549 | NixTerm::Function(_) => s.push_str("") 550 | }; 551 | Ok(()) 552 | } 553 | } 554 | 555 | impl<'state, 'val: 'state> Iterator for NixListIterator<'state, 'val> { 556 | type Item = NixResult>; 557 | 558 | fn next(&mut self) -> Option { 559 | if self.idx == self.len { 560 | return None; 561 | } 562 | let item = self.val.get_idx(self.idx); 563 | self.idx += 1; 564 | Some(item) 565 | } 566 | } 567 | 568 | impl<'state, 'val: 'state> Iterator for NixItemsIterator<'state, 'val> { 569 | type Item = (String, NixResult>); 570 | 571 | fn next(&mut self) -> Option { 572 | if self.idx == self.len { 573 | return None; 574 | } 575 | let raw = &self.val.0; 576 | let mut name: *const c_char = std::ptr::null(); 577 | let elem = unsafe { get_attr_byidx( 578 | raw._state.store.ctx.ptr(), 579 | raw.value.as_ptr(), 580 | raw._state.state_ptr(), 581 | self.idx as c_uint, 582 | &mut name 583 | )}; 584 | self.idx += 1; 585 | let name = unsafe { CStr::from_ptr(name) }.to_str().expect("Nix returned an invalid string").to_owned(); 586 | if let Err(err) = raw._state.store.ctx.check_call() { 587 | return Some((name, Err(err.into()))); 588 | }; 589 | let elem = NonNull::new(elem).expect("get_attr_byidx returned null"); 590 | let rawvalue = RawValue { 591 | _state: raw._state, 592 | value: elem 593 | }; 594 | Some((name, rawvalue.to_nix(&raw._state))) 595 | } 596 | } 597 | 598 | impl<'state, 'val: 'state> Iterator for NixNamesIterator<'state, 'val> { 599 | type Item = String; 600 | 601 | fn next(&mut self) -> Option { 602 | if self.idx == self.len { 603 | return None; 604 | } 605 | let raw = &self.val.0; 606 | let ctx = &raw._state.store.ctx; 607 | let name = unsafe { get_attr_name_byidx( 608 | ctx.ptr(), 609 | raw.value.as_ptr(), 610 | raw._state.state_ptr(), 611 | self.idx as c_uint 612 | )}; 613 | raw._state.store.ctx.check_call().expect("something went wrong"); 614 | let name = unsafe { CStr::from_ptr(name) }.to_str().expect("Nix returned an invalid string").to_owned(); 615 | self.idx += 1; 616 | Some(name) 617 | } 618 | } 619 | 620 | impl<'state> From for NixTerm<'state> { 621 | fn from(val: String) -> Self { 622 | NixTerm::String(val) 623 | } 624 | } 625 | 626 | impl<'state> From<&String> for NixTerm<'state> { 627 | fn from(val: &String) -> Self { 628 | NixTerm::String(val.clone()) 629 | } 630 | } 631 | 632 | impl<'state> From<&str> for NixTerm<'state> { 633 | fn from(val: &str) -> Self { 634 | NixTerm::String(val.to_string()) 635 | } 636 | } 637 | 638 | impl<'state> From for NixTerm<'state> { 639 | fn from(val: i64) -> Self { 640 | NixTerm::Int(val) 641 | } 642 | } 643 | 644 | impl<'state> From for NixTerm<'state> { 645 | fn from(val: PathBuf) -> Self { 646 | NixTerm::Path(val) 647 | } 648 | } 649 | 650 | impl<'state> From for NixTerm<'state> { 651 | fn from(val: bool) -> Self { 652 | NixTerm::Bool(val) 653 | } 654 | } 655 | 656 | impl<'state> From> for NixTerm<'state> { 657 | fn from(val: NixList<'state>) -> Self { 658 | NixTerm::List(val) 659 | } 660 | } 661 | 662 | impl<'state> From> for NixTerm<'state> { 663 | fn from(val: NixAttrSet<'state>) -> Self { 664 | NixTerm::AttrSet(val) 665 | } 666 | } 667 | 668 | impl<'state, N: ToNix<'state>> FromIterToNix<'state, N> for NixList<'state> { 669 | fn from_iter_to_nix(iter: T, state: &'state NixEvalState) -> NixResult 670 | where T: IntoIterator + ExactSizeIterator { 671 | let ctx = state.store.ctx.ptr(); 672 | let list_builder = unsafe { 673 | make_list_builder(ctx, state.state_ptr(), iter.len()) 674 | }; 675 | for (idx, elem) in iter.into_iter().enumerate() { 676 | let value = elem.to_nix(state)?.to_raw_value(state); 677 | unsafe { 678 | list_builder_insert(ctx, list_builder, idx as c_uint, value.value.as_ptr()); 679 | } 680 | state.store.ctx.check_call()?; 681 | } 682 | let value = RawValue::empty(state); 683 | unsafe { make_list(ctx, list_builder, value.value.as_ptr()) }; 684 | Ok(NixList(value)) 685 | } 686 | } 687 | impl<'state, S: AsRef, N: ToNix<'state>> FromIterToNix<'state, (S, N)> for NixAttrSet<'state> { 688 | fn from_iter_to_nix(iter: T, state: &'state NixEvalState) -> NixResult 689 | where T: IntoIterator + ExactSizeIterator { 690 | let ctx = state.store.ctx.ptr(); 691 | let bindings_builder = unsafe { 692 | make_bindings_builder(ctx, state.state_ptr(), iter.len()) 693 | }; 694 | state.store.ctx.check_call().unwrap(); 695 | for (key, val) in iter.into_iter() { 696 | let name = CString::new(key.as_ref()).expect("Key must be valid C string"); 697 | let value = val.to_nix(state)?.to_raw_value(state); 698 | unsafe { 699 | bindings_builder_insert(ctx, bindings_builder, name.as_ptr(), value.value.as_ptr()); 700 | } 701 | state.store.ctx.check_call().unwrap(); 702 | } 703 | let ctx = NixContext::default(); 704 | let value = RawValue::empty(state); 705 | unsafe { make_attrs(ctx.ptr(), value.value.as_ptr(), bindings_builder) }; 706 | ctx.check_call()?; 707 | unsafe { bindings_builder_free(bindings_builder); } 708 | ctx.check_call()?; 709 | Ok(NixAttrSet(value)) 710 | } 711 | } 712 | 713 | // TODO: re-implement without re-allocating 714 | impl<'state, E, O: FromIterToNix<'state, E>> FromIterToNix<'state, NixResult> for O { 715 | fn from_iter_to_nix(iter: T, state: &'state NixEvalState) -> NixResult 716 | where T: IntoIterator> + ExactSizeIterator { 717 | let iter: Vec = iter.into_iter().collect::>()?; 718 | iter.into_iter().collect_to_nix(state) 719 | } 720 | } 721 | 722 | // impl ToNix for F where F: Fn(NixTerm) -> NixResult { 723 | // fn to_nix<'s>(self, state: &'s NixEvalState) -> NixResult { 724 | // let context = &state.store.ctx; 725 | // let name = CString::new("rust-closure").unwrap(); 726 | // let doc = CString::new("rust closure").unwrap(); 727 | // let argname = CString::new("argument").unwrap(); 728 | // let mut args = Vec::from([argname.as_ptr(), std::ptr::null()]); 729 | // let box_closure = Box::new(Box::new(self)); 730 | // let ptr = Box::into_raw(box_closure); 731 | // let primop = unsafe { 732 | // alloc_primop(context.ptr(), 733 | // Some(call_rust_closure::), 734 | // 1, 735 | // name.as_ptr(), 736 | // args.as_mut_ptr(), 737 | // doc.as_ptr(), 738 | // ptr as *mut c_void 739 | // ) 740 | // }; 741 | // context.check_call().expect("Could not allocate primop"); 742 | // let value = RawValue::empty(state.clone()); 743 | // let tmp_ctx = NixContext::default(); 744 | // unsafe { 745 | // init_primop(tmp_ctx.ptr(), value.value.as_ptr(), primop); 746 | // }; 747 | // tmp_ctx.check_call().expect("Could not set primop"); 748 | // value.to_nix(state) 749 | // } 750 | 751 | // } 752 | 753 | impl From for NixEvalError { 754 | fn from(val: NixError) -> NixEvalError { 755 | NixEvalError::RuntimeError(val) 756 | } 757 | } 758 | 759 | impl From for NixEvalError { 760 | fn from(value: std::convert::Infallible) -> Self { 761 | match value {} 762 | } 763 | } 764 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.24.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 | 20 | [[package]] 21 | name = "ahash" 22 | version = "0.8.11" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" 25 | dependencies = [ 26 | "cfg-if", 27 | "once_cell", 28 | "version_check", 29 | "zerocopy", 30 | ] 31 | 32 | [[package]] 33 | name = "aho-corasick" 34 | version = "1.1.3" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 37 | dependencies = [ 38 | "memchr", 39 | ] 40 | 41 | [[package]] 42 | name = "allocator-api2" 43 | version = "0.2.21" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" 46 | 47 | [[package]] 48 | name = "anyhow" 49 | version = "1.0.94" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" 52 | 53 | [[package]] 54 | name = "arrayref" 55 | version = "0.3.9" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" 58 | 59 | [[package]] 60 | name = "arrayvec" 61 | version = "0.7.6" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" 64 | 65 | [[package]] 66 | name = "atoi" 67 | version = "2.0.0" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" 70 | dependencies = [ 71 | "num-traits", 72 | ] 73 | 74 | [[package]] 75 | name = "autocfg" 76 | version = "1.4.0" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 79 | 80 | [[package]] 81 | name = "backtrace" 82 | version = "0.3.74" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" 85 | dependencies = [ 86 | "addr2line", 87 | "cfg-if", 88 | "libc", 89 | "miniz_oxide", 90 | "object", 91 | "rustc-demangle", 92 | "windows-targets 0.52.6", 93 | ] 94 | 95 | [[package]] 96 | name = "base64" 97 | version = "0.22.1" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 100 | 101 | [[package]] 102 | name = "base64ct" 103 | version = "1.6.0" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" 106 | 107 | [[package]] 108 | name = "bindgen" 109 | version = "0.69.5" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" 112 | dependencies = [ 113 | "bitflags", 114 | "cexpr", 115 | "clang-sys", 116 | "itertools", 117 | "lazy_static", 118 | "lazycell", 119 | "log", 120 | "prettyplease", 121 | "proc-macro2", 122 | "quote", 123 | "regex", 124 | "rustc-hash", 125 | "shlex", 126 | "syn", 127 | "which", 128 | ] 129 | 130 | [[package]] 131 | name = "bitflags" 132 | version = "2.6.0" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 135 | dependencies = [ 136 | "serde", 137 | ] 138 | 139 | [[package]] 140 | name = "blake3" 141 | version = "1.5.5" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | checksum = "b8ee0c1824c4dea5b5f81736aff91bae041d2c07ee1192bec91054e10e3e601e" 144 | dependencies = [ 145 | "arrayref", 146 | "arrayvec", 147 | "cc", 148 | "cfg-if", 149 | "constant_time_eq", 150 | "memmap2", 151 | ] 152 | 153 | [[package]] 154 | name = "block-buffer" 155 | version = "0.10.4" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 158 | dependencies = [ 159 | "generic-array", 160 | ] 161 | 162 | [[package]] 163 | name = "byteorder" 164 | version = "1.5.0" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 167 | 168 | [[package]] 169 | name = "bytes" 170 | version = "1.9.0" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" 173 | 174 | [[package]] 175 | name = "cc" 176 | version = "1.2.3" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d" 179 | dependencies = [ 180 | "shlex", 181 | ] 182 | 183 | [[package]] 184 | name = "cexpr" 185 | version = "0.6.0" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" 188 | dependencies = [ 189 | "nom", 190 | ] 191 | 192 | [[package]] 193 | name = "cfg-if" 194 | version = "1.0.0" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 197 | 198 | [[package]] 199 | name = "cfg_aliases" 200 | version = "0.2.1" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 203 | 204 | [[package]] 205 | name = "clang-sys" 206 | version = "1.8.1" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" 209 | dependencies = [ 210 | "glob", 211 | "libc", 212 | "libloading", 213 | ] 214 | 215 | [[package]] 216 | name = "concurrent-queue" 217 | version = "2.5.0" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" 220 | dependencies = [ 221 | "crossbeam-utils", 222 | ] 223 | 224 | [[package]] 225 | name = "const-oid" 226 | version = "0.9.6" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" 229 | 230 | [[package]] 231 | name = "constant_time_eq" 232 | version = "0.3.1" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" 235 | 236 | [[package]] 237 | name = "cpufeatures" 238 | version = "0.2.16" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" 241 | dependencies = [ 242 | "libc", 243 | ] 244 | 245 | [[package]] 246 | name = "crc" 247 | version = "3.2.1" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" 250 | dependencies = [ 251 | "crc-catalog", 252 | ] 253 | 254 | [[package]] 255 | name = "crc-catalog" 256 | version = "2.4.0" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" 259 | 260 | [[package]] 261 | name = "crossbeam-queue" 262 | version = "0.3.11" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" 265 | dependencies = [ 266 | "crossbeam-utils", 267 | ] 268 | 269 | [[package]] 270 | name = "crossbeam-utils" 271 | version = "0.8.20" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" 274 | 275 | [[package]] 276 | name = "crypto-common" 277 | version = "0.1.6" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 280 | dependencies = [ 281 | "generic-array", 282 | "typenum", 283 | ] 284 | 285 | [[package]] 286 | name = "der" 287 | version = "0.7.9" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" 290 | dependencies = [ 291 | "const-oid", 292 | "pem-rfc7468", 293 | "zeroize", 294 | ] 295 | 296 | [[package]] 297 | name = "digest" 298 | version = "0.10.7" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 301 | dependencies = [ 302 | "block-buffer", 303 | "const-oid", 304 | "crypto-common", 305 | "subtle", 306 | ] 307 | 308 | [[package]] 309 | name = "displaydoc" 310 | version = "0.2.5" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 313 | dependencies = [ 314 | "proc-macro2", 315 | "quote", 316 | "syn", 317 | ] 318 | 319 | [[package]] 320 | name = "doctest-file" 321 | version = "1.0.0" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "aac81fa3e28d21450aa4d2ac065992ba96a1d7303efbce51a95f4fd175b67562" 324 | 325 | [[package]] 326 | name = "dotenvy" 327 | version = "0.15.7" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" 330 | 331 | [[package]] 332 | name = "either" 333 | version = "1.13.0" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" 336 | dependencies = [ 337 | "serde", 338 | ] 339 | 340 | [[package]] 341 | name = "equivalent" 342 | version = "1.0.1" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 345 | 346 | [[package]] 347 | name = "errno" 348 | version = "0.3.10" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" 351 | dependencies = [ 352 | "libc", 353 | "windows-sys 0.59.0", 354 | ] 355 | 356 | [[package]] 357 | name = "etcetera" 358 | version = "0.8.0" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" 361 | dependencies = [ 362 | "cfg-if", 363 | "home", 364 | "windows-sys 0.48.0", 365 | ] 366 | 367 | [[package]] 368 | name = "event-listener" 369 | version = "5.3.1" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" 372 | dependencies = [ 373 | "concurrent-queue", 374 | "parking", 375 | "pin-project-lite", 376 | ] 377 | 378 | [[package]] 379 | name = "fastrand" 380 | version = "2.3.0" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 383 | 384 | [[package]] 385 | name = "flume" 386 | version = "0.11.1" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" 389 | dependencies = [ 390 | "futures-core", 391 | "futures-sink", 392 | "spin", 393 | ] 394 | 395 | [[package]] 396 | name = "form_urlencoded" 397 | version = "1.2.1" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 400 | dependencies = [ 401 | "percent-encoding", 402 | ] 403 | 404 | [[package]] 405 | name = "futures-channel" 406 | version = "0.3.31" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 409 | dependencies = [ 410 | "futures-core", 411 | "futures-sink", 412 | ] 413 | 414 | [[package]] 415 | name = "futures-core" 416 | version = "0.3.31" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 419 | 420 | [[package]] 421 | name = "futures-executor" 422 | version = "0.3.31" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" 425 | dependencies = [ 426 | "futures-core", 427 | "futures-task", 428 | "futures-util", 429 | ] 430 | 431 | [[package]] 432 | name = "futures-intrusive" 433 | version = "0.5.0" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" 436 | dependencies = [ 437 | "futures-core", 438 | "lock_api", 439 | "parking_lot", 440 | ] 441 | 442 | [[package]] 443 | name = "futures-io" 444 | version = "0.3.31" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 447 | 448 | [[package]] 449 | name = "futures-macro" 450 | version = "0.3.31" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 453 | dependencies = [ 454 | "proc-macro2", 455 | "quote", 456 | "syn", 457 | ] 458 | 459 | [[package]] 460 | name = "futures-sink" 461 | version = "0.3.31" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 464 | 465 | [[package]] 466 | name = "futures-task" 467 | version = "0.3.31" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 470 | 471 | [[package]] 472 | name = "futures-util" 473 | version = "0.3.31" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 476 | dependencies = [ 477 | "futures-core", 478 | "futures-io", 479 | "futures-macro", 480 | "futures-sink", 481 | "futures-task", 482 | "memchr", 483 | "pin-project-lite", 484 | "pin-utils", 485 | "slab", 486 | ] 487 | 488 | [[package]] 489 | name = "generic-array" 490 | version = "0.14.7" 491 | source = "registry+https://github.com/rust-lang/crates.io-index" 492 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 493 | dependencies = [ 494 | "typenum", 495 | "version_check", 496 | ] 497 | 498 | [[package]] 499 | name = "getrandom" 500 | version = "0.2.15" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 503 | dependencies = [ 504 | "cfg-if", 505 | "libc", 506 | "wasi", 507 | ] 508 | 509 | [[package]] 510 | name = "gimli" 511 | version = "0.31.1" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 514 | 515 | [[package]] 516 | name = "glob" 517 | version = "0.3.1" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" 520 | 521 | [[package]] 522 | name = "hashbrown" 523 | version = "0.14.5" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 526 | dependencies = [ 527 | "ahash", 528 | "allocator-api2", 529 | ] 530 | 531 | [[package]] 532 | name = "hashbrown" 533 | version = "0.15.2" 534 | source = "registry+https://github.com/rust-lang/crates.io-index" 535 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 536 | 537 | [[package]] 538 | name = "hashlink" 539 | version = "0.9.1" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" 542 | dependencies = [ 543 | "hashbrown 0.14.5", 544 | ] 545 | 546 | [[package]] 547 | name = "heck" 548 | version = "0.5.0" 549 | source = "registry+https://github.com/rust-lang/crates.io-index" 550 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 551 | 552 | [[package]] 553 | name = "hermit-abi" 554 | version = "0.3.9" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 557 | 558 | [[package]] 559 | name = "hex" 560 | version = "0.4.3" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 563 | 564 | [[package]] 565 | name = "hkdf" 566 | version = "0.12.4" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" 569 | dependencies = [ 570 | "hmac", 571 | ] 572 | 573 | [[package]] 574 | name = "hmac" 575 | version = "0.12.1" 576 | source = "registry+https://github.com/rust-lang/crates.io-index" 577 | checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" 578 | dependencies = [ 579 | "digest", 580 | ] 581 | 582 | [[package]] 583 | name = "home" 584 | version = "0.5.9" 585 | source = "registry+https://github.com/rust-lang/crates.io-index" 586 | checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" 587 | dependencies = [ 588 | "windows-sys 0.52.0", 589 | ] 590 | 591 | [[package]] 592 | name = "icu_collections" 593 | version = "1.5.0" 594 | source = "registry+https://github.com/rust-lang/crates.io-index" 595 | checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" 596 | dependencies = [ 597 | "displaydoc", 598 | "yoke", 599 | "zerofrom", 600 | "zerovec", 601 | ] 602 | 603 | [[package]] 604 | name = "icu_locid" 605 | version = "1.5.0" 606 | source = "registry+https://github.com/rust-lang/crates.io-index" 607 | checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" 608 | dependencies = [ 609 | "displaydoc", 610 | "litemap", 611 | "tinystr", 612 | "writeable", 613 | "zerovec", 614 | ] 615 | 616 | [[package]] 617 | name = "icu_locid_transform" 618 | version = "1.5.0" 619 | source = "registry+https://github.com/rust-lang/crates.io-index" 620 | checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" 621 | dependencies = [ 622 | "displaydoc", 623 | "icu_locid", 624 | "icu_locid_transform_data", 625 | "icu_provider", 626 | "tinystr", 627 | "zerovec", 628 | ] 629 | 630 | [[package]] 631 | name = "icu_locid_transform_data" 632 | version = "1.5.0" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" 635 | 636 | [[package]] 637 | name = "icu_normalizer" 638 | version = "1.5.0" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" 641 | dependencies = [ 642 | "displaydoc", 643 | "icu_collections", 644 | "icu_normalizer_data", 645 | "icu_properties", 646 | "icu_provider", 647 | "smallvec", 648 | "utf16_iter", 649 | "utf8_iter", 650 | "write16", 651 | "zerovec", 652 | ] 653 | 654 | [[package]] 655 | name = "icu_normalizer_data" 656 | version = "1.5.0" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" 659 | 660 | [[package]] 661 | name = "icu_properties" 662 | version = "1.5.1" 663 | source = "registry+https://github.com/rust-lang/crates.io-index" 664 | checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" 665 | dependencies = [ 666 | "displaydoc", 667 | "icu_collections", 668 | "icu_locid_transform", 669 | "icu_properties_data", 670 | "icu_provider", 671 | "tinystr", 672 | "zerovec", 673 | ] 674 | 675 | [[package]] 676 | name = "icu_properties_data" 677 | version = "1.5.0" 678 | source = "registry+https://github.com/rust-lang/crates.io-index" 679 | checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" 680 | 681 | [[package]] 682 | name = "icu_provider" 683 | version = "1.5.0" 684 | source = "registry+https://github.com/rust-lang/crates.io-index" 685 | checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" 686 | dependencies = [ 687 | "displaydoc", 688 | "icu_locid", 689 | "icu_provider_macros", 690 | "stable_deref_trait", 691 | "tinystr", 692 | "writeable", 693 | "yoke", 694 | "zerofrom", 695 | "zerovec", 696 | ] 697 | 698 | [[package]] 699 | name = "icu_provider_macros" 700 | version = "1.5.0" 701 | source = "registry+https://github.com/rust-lang/crates.io-index" 702 | checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" 703 | dependencies = [ 704 | "proc-macro2", 705 | "quote", 706 | "syn", 707 | ] 708 | 709 | [[package]] 710 | name = "idna" 711 | version = "1.0.3" 712 | source = "registry+https://github.com/rust-lang/crates.io-index" 713 | checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 714 | dependencies = [ 715 | "idna_adapter", 716 | "smallvec", 717 | "utf8_iter", 718 | ] 719 | 720 | [[package]] 721 | name = "idna_adapter" 722 | version = "1.2.0" 723 | source = "registry+https://github.com/rust-lang/crates.io-index" 724 | checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" 725 | dependencies = [ 726 | "icu_normalizer", 727 | "icu_properties", 728 | ] 729 | 730 | [[package]] 731 | name = "indexmap" 732 | version = "2.7.0" 733 | source = "registry+https://github.com/rust-lang/crates.io-index" 734 | checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" 735 | dependencies = [ 736 | "equivalent", 737 | "hashbrown 0.15.2", 738 | ] 739 | 740 | [[package]] 741 | name = "indoc" 742 | version = "2.0.5" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" 745 | 746 | [[package]] 747 | name = "interprocess" 748 | version = "2.2.2" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "894148491d817cb36b6f778017b8ac46b17408d522dd90f539d677ea938362eb" 751 | dependencies = [ 752 | "doctest-file", 753 | "libc", 754 | "recvmsg", 755 | "widestring", 756 | "windows-sys 0.52.0", 757 | ] 758 | 759 | [[package]] 760 | name = "itertools" 761 | version = "0.12.1" 762 | source = "registry+https://github.com/rust-lang/crates.io-index" 763 | checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" 764 | dependencies = [ 765 | "either", 766 | ] 767 | 768 | [[package]] 769 | name = "itoa" 770 | version = "1.0.14" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" 773 | 774 | [[package]] 775 | name = "lazy_static" 776 | version = "1.5.0" 777 | source = "registry+https://github.com/rust-lang/crates.io-index" 778 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 779 | dependencies = [ 780 | "spin", 781 | ] 782 | 783 | [[package]] 784 | name = "lazycell" 785 | version = "1.3.0" 786 | source = "registry+https://github.com/rust-lang/crates.io-index" 787 | checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" 788 | 789 | [[package]] 790 | name = "libc" 791 | version = "0.2.172" 792 | source = "registry+https://github.com/rust-lang/crates.io-index" 793 | checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" 794 | 795 | [[package]] 796 | name = "libloading" 797 | version = "0.8.5" 798 | source = "registry+https://github.com/rust-lang/crates.io-index" 799 | checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" 800 | dependencies = [ 801 | "cfg-if", 802 | "windows-targets 0.52.6", 803 | ] 804 | 805 | [[package]] 806 | name = "libm" 807 | version = "0.2.11" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" 810 | 811 | [[package]] 812 | name = "libsqlite3-sys" 813 | version = "0.30.1" 814 | source = "registry+https://github.com/rust-lang/crates.io-index" 815 | checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" 816 | dependencies = [ 817 | "cc", 818 | "pkg-config", 819 | "vcpkg", 820 | ] 821 | 822 | [[package]] 823 | name = "linux-raw-sys" 824 | version = "0.4.14" 825 | source = "registry+https://github.com/rust-lang/crates.io-index" 826 | checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" 827 | 828 | [[package]] 829 | name = "litemap" 830 | version = "0.7.4" 831 | source = "registry+https://github.com/rust-lang/crates.io-index" 832 | checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" 833 | 834 | [[package]] 835 | name = "lock_api" 836 | version = "0.4.12" 837 | source = "registry+https://github.com/rust-lang/crates.io-index" 838 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 839 | dependencies = [ 840 | "autocfg", 841 | "scopeguard", 842 | ] 843 | 844 | [[package]] 845 | name = "log" 846 | version = "0.4.22" 847 | source = "registry+https://github.com/rust-lang/crates.io-index" 848 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 849 | 850 | [[package]] 851 | name = "md-5" 852 | version = "0.10.6" 853 | source = "registry+https://github.com/rust-lang/crates.io-index" 854 | checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" 855 | dependencies = [ 856 | "cfg-if", 857 | "digest", 858 | ] 859 | 860 | [[package]] 861 | name = "memchr" 862 | version = "2.7.4" 863 | source = "registry+https://github.com/rust-lang/crates.io-index" 864 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 865 | 866 | [[package]] 867 | name = "memmap2" 868 | version = "0.9.5" 869 | source = "registry+https://github.com/rust-lang/crates.io-index" 870 | checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" 871 | dependencies = [ 872 | "libc", 873 | ] 874 | 875 | [[package]] 876 | name = "memoffset" 877 | version = "0.9.1" 878 | source = "registry+https://github.com/rust-lang/crates.io-index" 879 | checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" 880 | dependencies = [ 881 | "autocfg", 882 | ] 883 | 884 | [[package]] 885 | name = "minimal-lexical" 886 | version = "0.2.1" 887 | source = "registry+https://github.com/rust-lang/crates.io-index" 888 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 889 | 890 | [[package]] 891 | name = "miniz_oxide" 892 | version = "0.8.0" 893 | source = "registry+https://github.com/rust-lang/crates.io-index" 894 | checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" 895 | dependencies = [ 896 | "adler2", 897 | ] 898 | 899 | [[package]] 900 | name = "mio" 901 | version = "1.0.2" 902 | source = "registry+https://github.com/rust-lang/crates.io-index" 903 | checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" 904 | dependencies = [ 905 | "hermit-abi", 906 | "libc", 907 | "wasi", 908 | "windows-sys 0.52.0", 909 | ] 910 | 911 | [[package]] 912 | name = "nix" 913 | version = "0.30.1" 914 | source = "registry+https://github.com/rust-lang/crates.io-index" 915 | checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" 916 | dependencies = [ 917 | "bitflags", 918 | "cfg-if", 919 | "cfg_aliases", 920 | "libc", 921 | ] 922 | 923 | [[package]] 924 | name = "nix-for-py" 925 | version = "0.1.0" 926 | dependencies = [ 927 | "anyhow", 928 | "nix-for-rust", 929 | "pyo3", 930 | ] 931 | 932 | [[package]] 933 | name = "nix-for-rust" 934 | version = "0.1.0" 935 | dependencies = [ 936 | "anyhow", 937 | "bindgen", 938 | "blake3", 939 | "futures-util", 940 | "home", 941 | "interprocess", 942 | "nix", 943 | "nom", 944 | "pkg-config", 945 | "sqlx", 946 | "thiserror", 947 | "tokio", 948 | ] 949 | 950 | [[package]] 951 | name = "nom" 952 | version = "7.1.3" 953 | source = "registry+https://github.com/rust-lang/crates.io-index" 954 | checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 955 | dependencies = [ 956 | "memchr", 957 | "minimal-lexical", 958 | ] 959 | 960 | [[package]] 961 | name = "num-bigint-dig" 962 | version = "0.8.4" 963 | source = "registry+https://github.com/rust-lang/crates.io-index" 964 | checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" 965 | dependencies = [ 966 | "byteorder", 967 | "lazy_static", 968 | "libm", 969 | "num-integer", 970 | "num-iter", 971 | "num-traits", 972 | "rand", 973 | "smallvec", 974 | "zeroize", 975 | ] 976 | 977 | [[package]] 978 | name = "num-integer" 979 | version = "0.1.46" 980 | source = "registry+https://github.com/rust-lang/crates.io-index" 981 | checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" 982 | dependencies = [ 983 | "num-traits", 984 | ] 985 | 986 | [[package]] 987 | name = "num-iter" 988 | version = "0.1.45" 989 | source = "registry+https://github.com/rust-lang/crates.io-index" 990 | checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" 991 | dependencies = [ 992 | "autocfg", 993 | "num-integer", 994 | "num-traits", 995 | ] 996 | 997 | [[package]] 998 | name = "num-traits" 999 | version = "0.2.19" 1000 | source = "registry+https://github.com/rust-lang/crates.io-index" 1001 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 1002 | dependencies = [ 1003 | "autocfg", 1004 | "libm", 1005 | ] 1006 | 1007 | [[package]] 1008 | name = "object" 1009 | version = "0.36.5" 1010 | source = "registry+https://github.com/rust-lang/crates.io-index" 1011 | checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" 1012 | dependencies = [ 1013 | "memchr", 1014 | ] 1015 | 1016 | [[package]] 1017 | name = "once_cell" 1018 | version = "1.20.2" 1019 | source = "registry+https://github.com/rust-lang/crates.io-index" 1020 | checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" 1021 | 1022 | [[package]] 1023 | name = "parking" 1024 | version = "2.2.1" 1025 | source = "registry+https://github.com/rust-lang/crates.io-index" 1026 | checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" 1027 | 1028 | [[package]] 1029 | name = "parking_lot" 1030 | version = "0.12.3" 1031 | source = "registry+https://github.com/rust-lang/crates.io-index" 1032 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 1033 | dependencies = [ 1034 | "lock_api", 1035 | "parking_lot_core", 1036 | ] 1037 | 1038 | [[package]] 1039 | name = "parking_lot_core" 1040 | version = "0.9.10" 1041 | source = "registry+https://github.com/rust-lang/crates.io-index" 1042 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 1043 | dependencies = [ 1044 | "cfg-if", 1045 | "libc", 1046 | "redox_syscall", 1047 | "smallvec", 1048 | "windows-targets 0.52.6", 1049 | ] 1050 | 1051 | [[package]] 1052 | name = "paste" 1053 | version = "1.0.15" 1054 | source = "registry+https://github.com/rust-lang/crates.io-index" 1055 | checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 1056 | 1057 | [[package]] 1058 | name = "pem-rfc7468" 1059 | version = "0.7.0" 1060 | source = "registry+https://github.com/rust-lang/crates.io-index" 1061 | checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" 1062 | dependencies = [ 1063 | "base64ct", 1064 | ] 1065 | 1066 | [[package]] 1067 | name = "percent-encoding" 1068 | version = "2.3.1" 1069 | source = "registry+https://github.com/rust-lang/crates.io-index" 1070 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 1071 | 1072 | [[package]] 1073 | name = "pin-project-lite" 1074 | version = "0.2.15" 1075 | source = "registry+https://github.com/rust-lang/crates.io-index" 1076 | checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" 1077 | 1078 | [[package]] 1079 | name = "pin-utils" 1080 | version = "0.1.0" 1081 | source = "registry+https://github.com/rust-lang/crates.io-index" 1082 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1083 | 1084 | [[package]] 1085 | name = "pkcs1" 1086 | version = "0.7.5" 1087 | source = "registry+https://github.com/rust-lang/crates.io-index" 1088 | checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" 1089 | dependencies = [ 1090 | "der", 1091 | "pkcs8", 1092 | "spki", 1093 | ] 1094 | 1095 | [[package]] 1096 | name = "pkcs8" 1097 | version = "0.10.2" 1098 | source = "registry+https://github.com/rust-lang/crates.io-index" 1099 | checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" 1100 | dependencies = [ 1101 | "der", 1102 | "spki", 1103 | ] 1104 | 1105 | [[package]] 1106 | name = "pkg-config" 1107 | version = "0.3.31" 1108 | source = "registry+https://github.com/rust-lang/crates.io-index" 1109 | checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" 1110 | 1111 | [[package]] 1112 | name = "portable-atomic" 1113 | version = "1.9.0" 1114 | source = "registry+https://github.com/rust-lang/crates.io-index" 1115 | checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" 1116 | 1117 | [[package]] 1118 | name = "ppv-lite86" 1119 | version = "0.2.20" 1120 | source = "registry+https://github.com/rust-lang/crates.io-index" 1121 | checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" 1122 | dependencies = [ 1123 | "zerocopy", 1124 | ] 1125 | 1126 | [[package]] 1127 | name = "prettyplease" 1128 | version = "0.2.22" 1129 | source = "registry+https://github.com/rust-lang/crates.io-index" 1130 | checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" 1131 | dependencies = [ 1132 | "proc-macro2", 1133 | "syn", 1134 | ] 1135 | 1136 | [[package]] 1137 | name = "proc-macro2" 1138 | version = "1.0.92" 1139 | source = "registry+https://github.com/rust-lang/crates.io-index" 1140 | checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" 1141 | dependencies = [ 1142 | "unicode-ident", 1143 | ] 1144 | 1145 | [[package]] 1146 | name = "pyo3" 1147 | version = "0.22.4" 1148 | source = "registry+https://github.com/rust-lang/crates.io-index" 1149 | checksum = "00e89ce2565d6044ca31a3eb79a334c3a79a841120a98f64eea9f579564cb691" 1150 | dependencies = [ 1151 | "anyhow", 1152 | "cfg-if", 1153 | "indoc", 1154 | "libc", 1155 | "memoffset", 1156 | "once_cell", 1157 | "portable-atomic", 1158 | "pyo3-build-config", 1159 | "pyo3-ffi", 1160 | "pyo3-macros", 1161 | "unindent", 1162 | ] 1163 | 1164 | [[package]] 1165 | name = "pyo3-build-config" 1166 | version = "0.22.4" 1167 | source = "registry+https://github.com/rust-lang/crates.io-index" 1168 | checksum = "d8afbaf3abd7325e08f35ffb8deb5892046fcb2608b703db6a583a5ba4cea01e" 1169 | dependencies = [ 1170 | "once_cell", 1171 | "target-lexicon", 1172 | ] 1173 | 1174 | [[package]] 1175 | name = "pyo3-ffi" 1176 | version = "0.22.4" 1177 | source = "registry+https://github.com/rust-lang/crates.io-index" 1178 | checksum = "ec15a5ba277339d04763f4c23d85987a5b08cbb494860be141e6a10a8eb88022" 1179 | dependencies = [ 1180 | "libc", 1181 | "pyo3-build-config", 1182 | ] 1183 | 1184 | [[package]] 1185 | name = "pyo3-macros" 1186 | version = "0.22.4" 1187 | source = "registry+https://github.com/rust-lang/crates.io-index" 1188 | checksum = "15e0f01b5364bcfbb686a52fc4181d412b708a68ed20c330db9fc8d2c2bf5a43" 1189 | dependencies = [ 1190 | "proc-macro2", 1191 | "pyo3-macros-backend", 1192 | "quote", 1193 | "syn", 1194 | ] 1195 | 1196 | [[package]] 1197 | name = "pyo3-macros-backend" 1198 | version = "0.22.4" 1199 | source = "registry+https://github.com/rust-lang/crates.io-index" 1200 | checksum = "a09b550200e1e5ed9176976d0060cbc2ea82dc8515da07885e7b8153a85caacb" 1201 | dependencies = [ 1202 | "heck", 1203 | "proc-macro2", 1204 | "pyo3-build-config", 1205 | "quote", 1206 | "syn", 1207 | ] 1208 | 1209 | [[package]] 1210 | name = "quote" 1211 | version = "1.0.37" 1212 | source = "registry+https://github.com/rust-lang/crates.io-index" 1213 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 1214 | dependencies = [ 1215 | "proc-macro2", 1216 | ] 1217 | 1218 | [[package]] 1219 | name = "rand" 1220 | version = "0.8.5" 1221 | source = "registry+https://github.com/rust-lang/crates.io-index" 1222 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1223 | dependencies = [ 1224 | "libc", 1225 | "rand_chacha", 1226 | "rand_core", 1227 | ] 1228 | 1229 | [[package]] 1230 | name = "rand_chacha" 1231 | version = "0.3.1" 1232 | source = "registry+https://github.com/rust-lang/crates.io-index" 1233 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1234 | dependencies = [ 1235 | "ppv-lite86", 1236 | "rand_core", 1237 | ] 1238 | 1239 | [[package]] 1240 | name = "rand_core" 1241 | version = "0.6.4" 1242 | source = "registry+https://github.com/rust-lang/crates.io-index" 1243 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1244 | dependencies = [ 1245 | "getrandom", 1246 | ] 1247 | 1248 | [[package]] 1249 | name = "recvmsg" 1250 | version = "1.0.0" 1251 | source = "registry+https://github.com/rust-lang/crates.io-index" 1252 | checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" 1253 | 1254 | [[package]] 1255 | name = "redox_syscall" 1256 | version = "0.5.7" 1257 | source = "registry+https://github.com/rust-lang/crates.io-index" 1258 | checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" 1259 | dependencies = [ 1260 | "bitflags", 1261 | ] 1262 | 1263 | [[package]] 1264 | name = "regex" 1265 | version = "1.11.0" 1266 | source = "registry+https://github.com/rust-lang/crates.io-index" 1267 | checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" 1268 | dependencies = [ 1269 | "aho-corasick", 1270 | "memchr", 1271 | "regex-automata", 1272 | "regex-syntax", 1273 | ] 1274 | 1275 | [[package]] 1276 | name = "regex-automata" 1277 | version = "0.4.8" 1278 | source = "registry+https://github.com/rust-lang/crates.io-index" 1279 | checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" 1280 | dependencies = [ 1281 | "aho-corasick", 1282 | "memchr", 1283 | "regex-syntax", 1284 | ] 1285 | 1286 | [[package]] 1287 | name = "regex-syntax" 1288 | version = "0.8.5" 1289 | source = "registry+https://github.com/rust-lang/crates.io-index" 1290 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 1291 | 1292 | [[package]] 1293 | name = "rsa" 1294 | version = "0.9.7" 1295 | source = "registry+https://github.com/rust-lang/crates.io-index" 1296 | checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519" 1297 | dependencies = [ 1298 | "const-oid", 1299 | "digest", 1300 | "num-bigint-dig", 1301 | "num-integer", 1302 | "num-traits", 1303 | "pkcs1", 1304 | "pkcs8", 1305 | "rand_core", 1306 | "signature", 1307 | "spki", 1308 | "subtle", 1309 | "zeroize", 1310 | ] 1311 | 1312 | [[package]] 1313 | name = "rustc-demangle" 1314 | version = "0.1.24" 1315 | source = "registry+https://github.com/rust-lang/crates.io-index" 1316 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 1317 | 1318 | [[package]] 1319 | name = "rustc-hash" 1320 | version = "1.1.0" 1321 | source = "registry+https://github.com/rust-lang/crates.io-index" 1322 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 1323 | 1324 | [[package]] 1325 | name = "rustix" 1326 | version = "0.38.42" 1327 | source = "registry+https://github.com/rust-lang/crates.io-index" 1328 | checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" 1329 | dependencies = [ 1330 | "bitflags", 1331 | "errno", 1332 | "libc", 1333 | "linux-raw-sys", 1334 | "windows-sys 0.59.0", 1335 | ] 1336 | 1337 | [[package]] 1338 | name = "ryu" 1339 | version = "1.0.18" 1340 | source = "registry+https://github.com/rust-lang/crates.io-index" 1341 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 1342 | 1343 | [[package]] 1344 | name = "scopeguard" 1345 | version = "1.2.0" 1346 | source = "registry+https://github.com/rust-lang/crates.io-index" 1347 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1348 | 1349 | [[package]] 1350 | name = "serde" 1351 | version = "1.0.216" 1352 | source = "registry+https://github.com/rust-lang/crates.io-index" 1353 | checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" 1354 | dependencies = [ 1355 | "serde_derive", 1356 | ] 1357 | 1358 | [[package]] 1359 | name = "serde_derive" 1360 | version = "1.0.216" 1361 | source = "registry+https://github.com/rust-lang/crates.io-index" 1362 | checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" 1363 | dependencies = [ 1364 | "proc-macro2", 1365 | "quote", 1366 | "syn", 1367 | ] 1368 | 1369 | [[package]] 1370 | name = "serde_json" 1371 | version = "1.0.133" 1372 | source = "registry+https://github.com/rust-lang/crates.io-index" 1373 | checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" 1374 | dependencies = [ 1375 | "itoa", 1376 | "memchr", 1377 | "ryu", 1378 | "serde", 1379 | ] 1380 | 1381 | [[package]] 1382 | name = "serde_urlencoded" 1383 | version = "0.7.1" 1384 | source = "registry+https://github.com/rust-lang/crates.io-index" 1385 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1386 | dependencies = [ 1387 | "form_urlencoded", 1388 | "itoa", 1389 | "ryu", 1390 | "serde", 1391 | ] 1392 | 1393 | [[package]] 1394 | name = "sha1" 1395 | version = "0.10.6" 1396 | source = "registry+https://github.com/rust-lang/crates.io-index" 1397 | checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" 1398 | dependencies = [ 1399 | "cfg-if", 1400 | "cpufeatures", 1401 | "digest", 1402 | ] 1403 | 1404 | [[package]] 1405 | name = "sha2" 1406 | version = "0.10.8" 1407 | source = "registry+https://github.com/rust-lang/crates.io-index" 1408 | checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" 1409 | dependencies = [ 1410 | "cfg-if", 1411 | "cpufeatures", 1412 | "digest", 1413 | ] 1414 | 1415 | [[package]] 1416 | name = "shlex" 1417 | version = "1.3.0" 1418 | source = "registry+https://github.com/rust-lang/crates.io-index" 1419 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1420 | 1421 | [[package]] 1422 | name = "signature" 1423 | version = "2.2.0" 1424 | source = "registry+https://github.com/rust-lang/crates.io-index" 1425 | checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" 1426 | dependencies = [ 1427 | "digest", 1428 | "rand_core", 1429 | ] 1430 | 1431 | [[package]] 1432 | name = "slab" 1433 | version = "0.4.9" 1434 | source = "registry+https://github.com/rust-lang/crates.io-index" 1435 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1436 | dependencies = [ 1437 | "autocfg", 1438 | ] 1439 | 1440 | [[package]] 1441 | name = "smallvec" 1442 | version = "1.13.2" 1443 | source = "registry+https://github.com/rust-lang/crates.io-index" 1444 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 1445 | dependencies = [ 1446 | "serde", 1447 | ] 1448 | 1449 | [[package]] 1450 | name = "socket2" 1451 | version = "0.5.7" 1452 | source = "registry+https://github.com/rust-lang/crates.io-index" 1453 | checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" 1454 | dependencies = [ 1455 | "libc", 1456 | "windows-sys 0.52.0", 1457 | ] 1458 | 1459 | [[package]] 1460 | name = "spin" 1461 | version = "0.9.8" 1462 | source = "registry+https://github.com/rust-lang/crates.io-index" 1463 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 1464 | dependencies = [ 1465 | "lock_api", 1466 | ] 1467 | 1468 | [[package]] 1469 | name = "spki" 1470 | version = "0.7.3" 1471 | source = "registry+https://github.com/rust-lang/crates.io-index" 1472 | checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" 1473 | dependencies = [ 1474 | "base64ct", 1475 | "der", 1476 | ] 1477 | 1478 | [[package]] 1479 | name = "sqlformat" 1480 | version = "0.2.6" 1481 | source = "registry+https://github.com/rust-lang/crates.io-index" 1482 | checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790" 1483 | dependencies = [ 1484 | "nom", 1485 | "unicode_categories", 1486 | ] 1487 | 1488 | [[package]] 1489 | name = "sqlx" 1490 | version = "0.8.2" 1491 | source = "registry+https://github.com/rust-lang/crates.io-index" 1492 | checksum = "93334716a037193fac19df402f8571269c84a00852f6a7066b5d2616dcd64d3e" 1493 | dependencies = [ 1494 | "sqlx-core", 1495 | "sqlx-macros", 1496 | "sqlx-mysql", 1497 | "sqlx-postgres", 1498 | "sqlx-sqlite", 1499 | ] 1500 | 1501 | [[package]] 1502 | name = "sqlx-core" 1503 | version = "0.8.2" 1504 | source = "registry+https://github.com/rust-lang/crates.io-index" 1505 | checksum = "d4d8060b456358185f7d50c55d9b5066ad956956fddec42ee2e8567134a8936e" 1506 | dependencies = [ 1507 | "atoi", 1508 | "byteorder", 1509 | "bytes", 1510 | "crc", 1511 | "crossbeam-queue", 1512 | "either", 1513 | "event-listener", 1514 | "futures-channel", 1515 | "futures-core", 1516 | "futures-intrusive", 1517 | "futures-io", 1518 | "futures-util", 1519 | "hashbrown 0.14.5", 1520 | "hashlink", 1521 | "hex", 1522 | "indexmap", 1523 | "log", 1524 | "memchr", 1525 | "once_cell", 1526 | "paste", 1527 | "percent-encoding", 1528 | "serde", 1529 | "serde_json", 1530 | "sha2", 1531 | "smallvec", 1532 | "sqlformat", 1533 | "thiserror", 1534 | "tokio", 1535 | "tokio-stream", 1536 | "tracing", 1537 | "url", 1538 | ] 1539 | 1540 | [[package]] 1541 | name = "sqlx-macros" 1542 | version = "0.8.2" 1543 | source = "registry+https://github.com/rust-lang/crates.io-index" 1544 | checksum = "cac0692bcc9de3b073e8d747391827297e075c7710ff6276d9f7a1f3d58c6657" 1545 | dependencies = [ 1546 | "proc-macro2", 1547 | "quote", 1548 | "sqlx-core", 1549 | "sqlx-macros-core", 1550 | "syn", 1551 | ] 1552 | 1553 | [[package]] 1554 | name = "sqlx-macros-core" 1555 | version = "0.8.2" 1556 | source = "registry+https://github.com/rust-lang/crates.io-index" 1557 | checksum = "1804e8a7c7865599c9c79be146dc8a9fd8cc86935fa641d3ea58e5f0688abaa5" 1558 | dependencies = [ 1559 | "dotenvy", 1560 | "either", 1561 | "heck", 1562 | "hex", 1563 | "once_cell", 1564 | "proc-macro2", 1565 | "quote", 1566 | "serde", 1567 | "serde_json", 1568 | "sha2", 1569 | "sqlx-core", 1570 | "sqlx-mysql", 1571 | "sqlx-postgres", 1572 | "sqlx-sqlite", 1573 | "syn", 1574 | "tempfile", 1575 | "tokio", 1576 | "url", 1577 | ] 1578 | 1579 | [[package]] 1580 | name = "sqlx-mysql" 1581 | version = "0.8.2" 1582 | source = "registry+https://github.com/rust-lang/crates.io-index" 1583 | checksum = "64bb4714269afa44aef2755150a0fc19d756fb580a67db8885608cf02f47d06a" 1584 | dependencies = [ 1585 | "atoi", 1586 | "base64", 1587 | "bitflags", 1588 | "byteorder", 1589 | "bytes", 1590 | "crc", 1591 | "digest", 1592 | "dotenvy", 1593 | "either", 1594 | "futures-channel", 1595 | "futures-core", 1596 | "futures-io", 1597 | "futures-util", 1598 | "generic-array", 1599 | "hex", 1600 | "hkdf", 1601 | "hmac", 1602 | "itoa", 1603 | "log", 1604 | "md-5", 1605 | "memchr", 1606 | "once_cell", 1607 | "percent-encoding", 1608 | "rand", 1609 | "rsa", 1610 | "serde", 1611 | "sha1", 1612 | "sha2", 1613 | "smallvec", 1614 | "sqlx-core", 1615 | "stringprep", 1616 | "thiserror", 1617 | "tracing", 1618 | "whoami", 1619 | ] 1620 | 1621 | [[package]] 1622 | name = "sqlx-postgres" 1623 | version = "0.8.2" 1624 | source = "registry+https://github.com/rust-lang/crates.io-index" 1625 | checksum = "6fa91a732d854c5d7726349bb4bb879bb9478993ceb764247660aee25f67c2f8" 1626 | dependencies = [ 1627 | "atoi", 1628 | "base64", 1629 | "bitflags", 1630 | "byteorder", 1631 | "crc", 1632 | "dotenvy", 1633 | "etcetera", 1634 | "futures-channel", 1635 | "futures-core", 1636 | "futures-io", 1637 | "futures-util", 1638 | "hex", 1639 | "hkdf", 1640 | "hmac", 1641 | "home", 1642 | "itoa", 1643 | "log", 1644 | "md-5", 1645 | "memchr", 1646 | "once_cell", 1647 | "rand", 1648 | "serde", 1649 | "serde_json", 1650 | "sha2", 1651 | "smallvec", 1652 | "sqlx-core", 1653 | "stringprep", 1654 | "thiserror", 1655 | "tracing", 1656 | "whoami", 1657 | ] 1658 | 1659 | [[package]] 1660 | name = "sqlx-sqlite" 1661 | version = "0.8.2" 1662 | source = "registry+https://github.com/rust-lang/crates.io-index" 1663 | checksum = "d5b2cf34a45953bfd3daaf3db0f7a7878ab9b7a6b91b422d24a7a9e4c857b680" 1664 | dependencies = [ 1665 | "atoi", 1666 | "flume", 1667 | "futures-channel", 1668 | "futures-core", 1669 | "futures-executor", 1670 | "futures-intrusive", 1671 | "futures-util", 1672 | "libsqlite3-sys", 1673 | "log", 1674 | "percent-encoding", 1675 | "serde", 1676 | "serde_urlencoded", 1677 | "sqlx-core", 1678 | "tracing", 1679 | "url", 1680 | ] 1681 | 1682 | [[package]] 1683 | name = "stable_deref_trait" 1684 | version = "1.2.0" 1685 | source = "registry+https://github.com/rust-lang/crates.io-index" 1686 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1687 | 1688 | [[package]] 1689 | name = "stringprep" 1690 | version = "0.1.5" 1691 | source = "registry+https://github.com/rust-lang/crates.io-index" 1692 | checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" 1693 | dependencies = [ 1694 | "unicode-bidi", 1695 | "unicode-normalization", 1696 | "unicode-properties", 1697 | ] 1698 | 1699 | [[package]] 1700 | name = "subtle" 1701 | version = "2.6.1" 1702 | source = "registry+https://github.com/rust-lang/crates.io-index" 1703 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 1704 | 1705 | [[package]] 1706 | name = "syn" 1707 | version = "2.0.90" 1708 | source = "registry+https://github.com/rust-lang/crates.io-index" 1709 | checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" 1710 | dependencies = [ 1711 | "proc-macro2", 1712 | "quote", 1713 | "unicode-ident", 1714 | ] 1715 | 1716 | [[package]] 1717 | name = "synstructure" 1718 | version = "0.13.1" 1719 | source = "registry+https://github.com/rust-lang/crates.io-index" 1720 | checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" 1721 | dependencies = [ 1722 | "proc-macro2", 1723 | "quote", 1724 | "syn", 1725 | ] 1726 | 1727 | [[package]] 1728 | name = "target-lexicon" 1729 | version = "0.12.16" 1730 | source = "registry+https://github.com/rust-lang/crates.io-index" 1731 | checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" 1732 | 1733 | [[package]] 1734 | name = "tempfile" 1735 | version = "3.14.0" 1736 | source = "registry+https://github.com/rust-lang/crates.io-index" 1737 | checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" 1738 | dependencies = [ 1739 | "cfg-if", 1740 | "fastrand", 1741 | "once_cell", 1742 | "rustix", 1743 | "windows-sys 0.59.0", 1744 | ] 1745 | 1746 | [[package]] 1747 | name = "thiserror" 1748 | version = "1.0.64" 1749 | source = "registry+https://github.com/rust-lang/crates.io-index" 1750 | checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" 1751 | dependencies = [ 1752 | "thiserror-impl", 1753 | ] 1754 | 1755 | [[package]] 1756 | name = "thiserror-impl" 1757 | version = "1.0.64" 1758 | source = "registry+https://github.com/rust-lang/crates.io-index" 1759 | checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" 1760 | dependencies = [ 1761 | "proc-macro2", 1762 | "quote", 1763 | "syn", 1764 | ] 1765 | 1766 | [[package]] 1767 | name = "tinystr" 1768 | version = "0.7.6" 1769 | source = "registry+https://github.com/rust-lang/crates.io-index" 1770 | checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" 1771 | dependencies = [ 1772 | "displaydoc", 1773 | "zerovec", 1774 | ] 1775 | 1776 | [[package]] 1777 | name = "tinyvec" 1778 | version = "1.8.0" 1779 | source = "registry+https://github.com/rust-lang/crates.io-index" 1780 | checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" 1781 | dependencies = [ 1782 | "tinyvec_macros", 1783 | ] 1784 | 1785 | [[package]] 1786 | name = "tinyvec_macros" 1787 | version = "0.1.1" 1788 | source = "registry+https://github.com/rust-lang/crates.io-index" 1789 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 1790 | 1791 | [[package]] 1792 | name = "tokio" 1793 | version = "1.42.0" 1794 | source = "registry+https://github.com/rust-lang/crates.io-index" 1795 | checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" 1796 | dependencies = [ 1797 | "backtrace", 1798 | "bytes", 1799 | "libc", 1800 | "mio", 1801 | "pin-project-lite", 1802 | "socket2", 1803 | "windows-sys 0.52.0", 1804 | ] 1805 | 1806 | [[package]] 1807 | name = "tokio-stream" 1808 | version = "0.1.16" 1809 | source = "registry+https://github.com/rust-lang/crates.io-index" 1810 | checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" 1811 | dependencies = [ 1812 | "futures-core", 1813 | "pin-project-lite", 1814 | "tokio", 1815 | ] 1816 | 1817 | [[package]] 1818 | name = "tracing" 1819 | version = "0.1.41" 1820 | source = "registry+https://github.com/rust-lang/crates.io-index" 1821 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 1822 | dependencies = [ 1823 | "log", 1824 | "pin-project-lite", 1825 | "tracing-attributes", 1826 | "tracing-core", 1827 | ] 1828 | 1829 | [[package]] 1830 | name = "tracing-attributes" 1831 | version = "0.1.28" 1832 | source = "registry+https://github.com/rust-lang/crates.io-index" 1833 | checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" 1834 | dependencies = [ 1835 | "proc-macro2", 1836 | "quote", 1837 | "syn", 1838 | ] 1839 | 1840 | [[package]] 1841 | name = "tracing-core" 1842 | version = "0.1.33" 1843 | source = "registry+https://github.com/rust-lang/crates.io-index" 1844 | checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" 1845 | dependencies = [ 1846 | "once_cell", 1847 | ] 1848 | 1849 | [[package]] 1850 | name = "typenum" 1851 | version = "1.17.0" 1852 | source = "registry+https://github.com/rust-lang/crates.io-index" 1853 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 1854 | 1855 | [[package]] 1856 | name = "unicode-bidi" 1857 | version = "0.3.17" 1858 | source = "registry+https://github.com/rust-lang/crates.io-index" 1859 | checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" 1860 | 1861 | [[package]] 1862 | name = "unicode-ident" 1863 | version = "1.0.13" 1864 | source = "registry+https://github.com/rust-lang/crates.io-index" 1865 | checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" 1866 | 1867 | [[package]] 1868 | name = "unicode-normalization" 1869 | version = "0.1.24" 1870 | source = "registry+https://github.com/rust-lang/crates.io-index" 1871 | checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" 1872 | dependencies = [ 1873 | "tinyvec", 1874 | ] 1875 | 1876 | [[package]] 1877 | name = "unicode-properties" 1878 | version = "0.1.3" 1879 | source = "registry+https://github.com/rust-lang/crates.io-index" 1880 | checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" 1881 | 1882 | [[package]] 1883 | name = "unicode_categories" 1884 | version = "0.1.1" 1885 | source = "registry+https://github.com/rust-lang/crates.io-index" 1886 | checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" 1887 | 1888 | [[package]] 1889 | name = "unindent" 1890 | version = "0.2.3" 1891 | source = "registry+https://github.com/rust-lang/crates.io-index" 1892 | checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" 1893 | 1894 | [[package]] 1895 | name = "url" 1896 | version = "2.5.4" 1897 | source = "registry+https://github.com/rust-lang/crates.io-index" 1898 | checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" 1899 | dependencies = [ 1900 | "form_urlencoded", 1901 | "idna", 1902 | "percent-encoding", 1903 | ] 1904 | 1905 | [[package]] 1906 | name = "utf16_iter" 1907 | version = "1.0.5" 1908 | source = "registry+https://github.com/rust-lang/crates.io-index" 1909 | checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" 1910 | 1911 | [[package]] 1912 | name = "utf8_iter" 1913 | version = "1.0.4" 1914 | source = "registry+https://github.com/rust-lang/crates.io-index" 1915 | checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 1916 | 1917 | [[package]] 1918 | name = "vcpkg" 1919 | version = "0.2.15" 1920 | source = "registry+https://github.com/rust-lang/crates.io-index" 1921 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 1922 | 1923 | [[package]] 1924 | name = "version_check" 1925 | version = "0.9.5" 1926 | source = "registry+https://github.com/rust-lang/crates.io-index" 1927 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 1928 | 1929 | [[package]] 1930 | name = "wasi" 1931 | version = "0.11.0+wasi-snapshot-preview1" 1932 | source = "registry+https://github.com/rust-lang/crates.io-index" 1933 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1934 | 1935 | [[package]] 1936 | name = "wasite" 1937 | version = "0.1.0" 1938 | source = "registry+https://github.com/rust-lang/crates.io-index" 1939 | checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" 1940 | 1941 | [[package]] 1942 | name = "which" 1943 | version = "4.4.2" 1944 | source = "registry+https://github.com/rust-lang/crates.io-index" 1945 | checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" 1946 | dependencies = [ 1947 | "either", 1948 | "home", 1949 | "once_cell", 1950 | "rustix", 1951 | ] 1952 | 1953 | [[package]] 1954 | name = "whoami" 1955 | version = "1.5.2" 1956 | source = "registry+https://github.com/rust-lang/crates.io-index" 1957 | checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" 1958 | dependencies = [ 1959 | "redox_syscall", 1960 | "wasite", 1961 | ] 1962 | 1963 | [[package]] 1964 | name = "widestring" 1965 | version = "1.1.0" 1966 | source = "registry+https://github.com/rust-lang/crates.io-index" 1967 | checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" 1968 | 1969 | [[package]] 1970 | name = "windows-sys" 1971 | version = "0.48.0" 1972 | source = "registry+https://github.com/rust-lang/crates.io-index" 1973 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 1974 | dependencies = [ 1975 | "windows-targets 0.48.5", 1976 | ] 1977 | 1978 | [[package]] 1979 | name = "windows-sys" 1980 | version = "0.52.0" 1981 | source = "registry+https://github.com/rust-lang/crates.io-index" 1982 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1983 | dependencies = [ 1984 | "windows-targets 0.52.6", 1985 | ] 1986 | 1987 | [[package]] 1988 | name = "windows-sys" 1989 | version = "0.59.0" 1990 | source = "registry+https://github.com/rust-lang/crates.io-index" 1991 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 1992 | dependencies = [ 1993 | "windows-targets 0.52.6", 1994 | ] 1995 | 1996 | [[package]] 1997 | name = "windows-targets" 1998 | version = "0.48.5" 1999 | source = "registry+https://github.com/rust-lang/crates.io-index" 2000 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 2001 | dependencies = [ 2002 | "windows_aarch64_gnullvm 0.48.5", 2003 | "windows_aarch64_msvc 0.48.5", 2004 | "windows_i686_gnu 0.48.5", 2005 | "windows_i686_msvc 0.48.5", 2006 | "windows_x86_64_gnu 0.48.5", 2007 | "windows_x86_64_gnullvm 0.48.5", 2008 | "windows_x86_64_msvc 0.48.5", 2009 | ] 2010 | 2011 | [[package]] 2012 | name = "windows-targets" 2013 | version = "0.52.6" 2014 | source = "registry+https://github.com/rust-lang/crates.io-index" 2015 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 2016 | dependencies = [ 2017 | "windows_aarch64_gnullvm 0.52.6", 2018 | "windows_aarch64_msvc 0.52.6", 2019 | "windows_i686_gnu 0.52.6", 2020 | "windows_i686_gnullvm", 2021 | "windows_i686_msvc 0.52.6", 2022 | "windows_x86_64_gnu 0.52.6", 2023 | "windows_x86_64_gnullvm 0.52.6", 2024 | "windows_x86_64_msvc 0.52.6", 2025 | ] 2026 | 2027 | [[package]] 2028 | name = "windows_aarch64_gnullvm" 2029 | version = "0.48.5" 2030 | source = "registry+https://github.com/rust-lang/crates.io-index" 2031 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 2032 | 2033 | [[package]] 2034 | name = "windows_aarch64_gnullvm" 2035 | version = "0.52.6" 2036 | source = "registry+https://github.com/rust-lang/crates.io-index" 2037 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 2038 | 2039 | [[package]] 2040 | name = "windows_aarch64_msvc" 2041 | version = "0.48.5" 2042 | source = "registry+https://github.com/rust-lang/crates.io-index" 2043 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 2044 | 2045 | [[package]] 2046 | name = "windows_aarch64_msvc" 2047 | version = "0.52.6" 2048 | source = "registry+https://github.com/rust-lang/crates.io-index" 2049 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 2050 | 2051 | [[package]] 2052 | name = "windows_i686_gnu" 2053 | version = "0.48.5" 2054 | source = "registry+https://github.com/rust-lang/crates.io-index" 2055 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 2056 | 2057 | [[package]] 2058 | name = "windows_i686_gnu" 2059 | version = "0.52.6" 2060 | source = "registry+https://github.com/rust-lang/crates.io-index" 2061 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 2062 | 2063 | [[package]] 2064 | name = "windows_i686_gnullvm" 2065 | version = "0.52.6" 2066 | source = "registry+https://github.com/rust-lang/crates.io-index" 2067 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 2068 | 2069 | [[package]] 2070 | name = "windows_i686_msvc" 2071 | version = "0.48.5" 2072 | source = "registry+https://github.com/rust-lang/crates.io-index" 2073 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 2074 | 2075 | [[package]] 2076 | name = "windows_i686_msvc" 2077 | version = "0.52.6" 2078 | source = "registry+https://github.com/rust-lang/crates.io-index" 2079 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 2080 | 2081 | [[package]] 2082 | name = "windows_x86_64_gnu" 2083 | version = "0.48.5" 2084 | source = "registry+https://github.com/rust-lang/crates.io-index" 2085 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 2086 | 2087 | [[package]] 2088 | name = "windows_x86_64_gnu" 2089 | version = "0.52.6" 2090 | source = "registry+https://github.com/rust-lang/crates.io-index" 2091 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 2092 | 2093 | [[package]] 2094 | name = "windows_x86_64_gnullvm" 2095 | version = "0.48.5" 2096 | source = "registry+https://github.com/rust-lang/crates.io-index" 2097 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 2098 | 2099 | [[package]] 2100 | name = "windows_x86_64_gnullvm" 2101 | version = "0.52.6" 2102 | source = "registry+https://github.com/rust-lang/crates.io-index" 2103 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 2104 | 2105 | [[package]] 2106 | name = "windows_x86_64_msvc" 2107 | version = "0.48.5" 2108 | source = "registry+https://github.com/rust-lang/crates.io-index" 2109 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 2110 | 2111 | [[package]] 2112 | name = "windows_x86_64_msvc" 2113 | version = "0.52.6" 2114 | source = "registry+https://github.com/rust-lang/crates.io-index" 2115 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 2116 | 2117 | [[package]] 2118 | name = "write16" 2119 | version = "1.0.0" 2120 | source = "registry+https://github.com/rust-lang/crates.io-index" 2121 | checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" 2122 | 2123 | [[package]] 2124 | name = "writeable" 2125 | version = "0.5.5" 2126 | source = "registry+https://github.com/rust-lang/crates.io-index" 2127 | checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" 2128 | 2129 | [[package]] 2130 | name = "yoke" 2131 | version = "0.7.5" 2132 | source = "registry+https://github.com/rust-lang/crates.io-index" 2133 | checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" 2134 | dependencies = [ 2135 | "serde", 2136 | "stable_deref_trait", 2137 | "yoke-derive", 2138 | "zerofrom", 2139 | ] 2140 | 2141 | [[package]] 2142 | name = "yoke-derive" 2143 | version = "0.7.5" 2144 | source = "registry+https://github.com/rust-lang/crates.io-index" 2145 | checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" 2146 | dependencies = [ 2147 | "proc-macro2", 2148 | "quote", 2149 | "syn", 2150 | "synstructure", 2151 | ] 2152 | 2153 | [[package]] 2154 | name = "zerocopy" 2155 | version = "0.7.35" 2156 | source = "registry+https://github.com/rust-lang/crates.io-index" 2157 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 2158 | dependencies = [ 2159 | "byteorder", 2160 | "zerocopy-derive", 2161 | ] 2162 | 2163 | [[package]] 2164 | name = "zerocopy-derive" 2165 | version = "0.7.35" 2166 | source = "registry+https://github.com/rust-lang/crates.io-index" 2167 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 2168 | dependencies = [ 2169 | "proc-macro2", 2170 | "quote", 2171 | "syn", 2172 | ] 2173 | 2174 | [[package]] 2175 | name = "zerofrom" 2176 | version = "0.1.5" 2177 | source = "registry+https://github.com/rust-lang/crates.io-index" 2178 | checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" 2179 | dependencies = [ 2180 | "zerofrom-derive", 2181 | ] 2182 | 2183 | [[package]] 2184 | name = "zerofrom-derive" 2185 | version = "0.1.5" 2186 | source = "registry+https://github.com/rust-lang/crates.io-index" 2187 | checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" 2188 | dependencies = [ 2189 | "proc-macro2", 2190 | "quote", 2191 | "syn", 2192 | "synstructure", 2193 | ] 2194 | 2195 | [[package]] 2196 | name = "zeroize" 2197 | version = "1.8.1" 2198 | source = "registry+https://github.com/rust-lang/crates.io-index" 2199 | checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 2200 | 2201 | [[package]] 2202 | name = "zerovec" 2203 | version = "0.10.4" 2204 | source = "registry+https://github.com/rust-lang/crates.io-index" 2205 | checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" 2206 | dependencies = [ 2207 | "yoke", 2208 | "zerofrom", 2209 | "zerovec-derive", 2210 | ] 2211 | 2212 | [[package]] 2213 | name = "zerovec-derive" 2214 | version = "0.10.3" 2215 | source = "registry+https://github.com/rust-lang/crates.io-index" 2216 | checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" 2217 | dependencies = [ 2218 | "proc-macro2", 2219 | "quote", 2220 | "syn", 2221 | ] 2222 | --------------------------------------------------------------------------------