Compare commits

..

No commits in common. "5930db8233aa8fb384b873fe855b3b2980d8aea4" and "77563659416187916b73cddc96f0e122ac5ad2fd" have entirely different histories.

16 changed files with 65 additions and 292 deletions

32
Cargo.lock generated
View file

@ -72,22 +72,6 @@ dependencies = [
"libloading", "libloading",
] ]
[[package]]
name = "ctor"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "424e0138278faeb2b401f174ad17e715c829512d74f3d1e81eb43365c2e0590e"
dependencies = [
"ctor-proc-macro",
"dtor",
]
[[package]]
name = "ctor-proc-macro"
version = "0.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1"
[[package]] [[package]]
name = "doxygen-bindgen" name = "doxygen-bindgen"
version = "0.1.3" version = "0.1.3"
@ -97,21 +81,6 @@ dependencies = [
"yap", "yap",
] ]
[[package]]
name = "dtor"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "404d02eeb088a82cfd873006cb713fe411306c7d182c344905e101fb1167d301"
dependencies = [
"dtor-proc-macro",
]
[[package]]
name = "dtor-proc-macro"
version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5"
[[package]] [[package]]
name = "either" name = "either"
version = "1.15.0" version = "1.15.0"
@ -221,7 +190,6 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
name = "nixide" name = "nixide"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"ctor",
"libc", "libc",
"nixide-sys", "nixide-sys",
"serial_test", "serial_test",

View file

@ -19,16 +19,14 @@ targets = [ "x86_64-unknown-linux-gnu" ]
[lib] [lib]
path = "lib.rs" path = "lib.rs"
# NOTE: `[features]` have a 1-1 correspondence to the
# NOTE: shared libraries produced by the Nix C API.
[features] [features]
default = ["nix-util-c"] default = ["util"]
nix-util-c = [] expr = []
nix-store-c = [] fetchers = []
nix-expr-c = [] flakes = []
nix-fetchers-c = [] store = []
nix-flake-c = [] util = []
nix-main-c = [] gc = []
[build-dependencies] [build-dependencies]
bindgen = { default-features = false, features = [ "logging", "runtime" ], version = "0.72.1" } bindgen = { default-features = false, features = [ "logging", "runtime" ], version = "0.72.1" }

View file

@ -20,26 +20,17 @@ impl ParseCallbacks for DoxygenCallbacks {
fn main() { fn main() {
// Invalidate the built crate whenever the wrapper changes // Invalidate the built crate whenever the wrapper changes
println!("cargo:rerun-if-changed=include/nix-util.h"); println!("cargo:rerun-if-changed=include/wrapper.h");
println!("cargo:rerun-if-changed=include/nix-store.h");
println!("cargo:rerun-if-changed=include/nix-expr.h");
println!("cargo:rerun-if-changed=include/nix-fetchers.h");
println!("cargo:rerun-if-changed=include/nix-flake.h");
println!("cargo:rerun-if-changed=include/nix-main.h");
// Use pkg-config to find nix-store include and link paths
// This NEEDS to be included, or otherwise `nix_api_store.h` cannot
// be found.
let libs = [ let libs = [
#[cfg(feature = "nix-util-c")]
"nix-util-c",
#[cfg(feature = "nix-store-c")]
"nix-store-c",
#[cfg(feature = "nix-expr-c")]
"nix-expr-c",
#[cfg(feature = "nix-fetchers-c")]
"nix-fetchers-c",
#[cfg(feature = "nix-flake-c")]
"nix-flake-c",
#[cfg(feature = "nix-main-c")]
"nix-main-c", "nix-main-c",
"nix-expr-c",
"nix-store-c",
"nix-util-c",
"nix-flake-c",
]; ];
let lib_args: Vec<String> = libs let lib_args: Vec<String> = libs
@ -59,43 +50,17 @@ fn main() {
.flatten() .flatten()
.collect(); .collect();
let mut builder = bindgen::Builder::default() let bindings = bindgen::Builder::default()
.clang_args(lib_args) .clang_args(lib_args)
// The input header we would like to generate bindings for
.header("include/wrapper.h")
// Invalidate the built crate when an included header file changes // Invalidate the built crate when an included header file changes
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
// Add `doxygen_bindgen` callbacks // Add `doxygen_bindgen` callbacks
.parse_callbacks(Box::new(DoxygenCallbacks)) .parse_callbacks(Box::new(DoxygenCallbacks))
// Format generated bindings with rustfmt // Format generated bindings with rustfmt
.formatter(bindgen::Formatter::Rustfmt) .formatter(bindgen::Formatter::Rustfmt)
.rustfmt_configuration_file(std::fs::canonicalize(".rustfmt.toml").ok()); .rustfmt_configuration_file(std::fs::canonicalize(".rustfmt.toml").ok())
// The input headers we would like to generate bindings for
#[cfg(feature = "nix-util-c")]
{
builder = builder.header("include/nix-util.h")
}
#[cfg(feature = "nix-store-c")]
{
builder = builder.header("include/nix-store.h")
}
#[cfg(feature = "nix-expr-c")]
{
builder = builder.header("include/nix-expr.h")
}
#[cfg(feature = "nix-fetchers-c")]
{
builder = builder.header("include/nix-fetchers.h")
}
#[cfg(feature = "nix-flake-c")]
{
builder = builder.header("include/nix-flake.h")
}
#[cfg(feature = "nix-main-c")]
{
builder = builder.header("include/nix-main.h")
}
let bindings = builder
// Finish the builder and generate the bindings // Finish the builder and generate the bindings
.generate() .generate()
// Unwrap the Result and panic on failure // Unwrap the Result and panic on failure

View file

@ -1,10 +0,0 @@
// Nix C API for the Nix expressions evaluator.
#include <nix_api_expr.h>
// Nix C API for value manipulation.
//
#include <nix_api_value.h>
// Nix C API for external values.
//
#include <nix_api_external.h>

View file

@ -1,3 +0,0 @@
// Nix C API for fetcher operations.
//
#include <nix_api_fetchers.h>

View file

@ -1,3 +0,0 @@
// Nix C API for flake support.
//
#include <nix_api_flake.h>

View file

@ -1,3 +0,0 @@
// Nix C API for CLI support.
//
#include <nix_api_main.h>

View file

@ -1,3 +0,0 @@
// Nix C API for store operations.
//
#include <nix_api_store.h>

View file

@ -1,7 +0,0 @@
// Nix C API for utilities.
//
// Most notably containing functions for handling
// the `nix_c_context` structure, which is used throughout
// the Nix C APIs for error handling.
//
#include <nix_api_util.h>

View file

@ -0,0 +1,23 @@
// Pure C API for store operations
#include <nix_api_store.h>
// Pure C API for error handling
#include <nix_api_util.h>
// Pure C API for the Nix evaluator
#include <nix_api_expr.h>
// Pure C API for external values
#include <nix_api_external.h>
// Pure C API for value manipulation
#include <nix_api_value.h>
// Pure C API for fetcher operations
#include <nix_api_fetchers.h>
// Pure C API for flake support
#include <nix_api_flake.h>
// Pure C API for main/CLI support
#include <nix_api_main.h>

View file

@ -15,16 +15,18 @@ edition = "2024"
path = "src/lib.rs" path = "src/lib.rs"
[features] [features]
default = [] default = ["util"]
store = ["nixide-sys/nix-store-c"] expr = []
expr = ["store", "nixide-sys/nix-expr-c"] fetchers = []
flake = ["store", "nixide-sys/nix-flake-c", "nixide-sys/nix-fetchers-c"] flakes = []
store = []
util = []
gc = []
[dependencies] [dependencies]
libc = "0.2.183" libc = "0.2.183"
stdext = "0.3.3" stdext = "0.3.3"
ctor = "0.6.3" nixide-sys = { path = "../nixide-sys", version = "0.1.0" }
nixide-sys = { path = "../nixide-sys", version = "0.1.0", features = ["nix-util-c", "nix-main-c"]}
[dev-dependencies] [dev-dependencies]
serial_test = "3.4.0" serial_test = "3.4.0"

View file

@ -180,36 +180,11 @@ impl ErrorContext {
/// `nix_clear_err` only modifies the `last_err_code`, it does not /// `nix_clear_err` only modifies the `last_err_code`, it does not
/// clear all attributes of a `nix_c_context` struct. Hence all uses /// clear all attributes of a `nix_c_context` struct. Hence all uses
/// of `nix_c_context` must be careful to check the `last_err_code` regularly. /// of `nix_c_context` must be careful to check the `last_err_code` regularly.
///
pub fn clear(&mut self) { pub fn clear(&mut self) {
unsafe { unsafe {
sys::nix_clear_err(self.as_ptr()); sys::nix_clear_err(self.as_ptr());
} }
} }
/// This function never fails.
/// Nixide will always guarantee `context != nullptr`.
///
/// # Nix C++ API Internals
///
/// ```cpp
/// nix_err nix_set_err_msg(nix_c_context * context, nix_err err, const char * msg)
/// {
/// if (context == nullptr) {
/// // todo last_err_code
/// throw nix::Error("Nix C api error: %s", msg);
/// }
/// context->last_err_code = err;
/// context->last_err = msg;
/// return err;
/// }
/// ```
///
pub fn set_err(&self, err: NixError, msg: &str) {
let ptr = unsafe { self.as_ptr() };
assert!(!ptr.is_null(), "");
sys::nix_set_err_msg(ptr, err.into())
}
/// Returns [None] if [self.code] is [sys::nix_err_NIX_OK], and [Some] otherwise. /// Returns [None] if [self.code] is [sys::nix_err_NIX_OK], and [Some] otherwise.
/// ///

View file

@ -1,93 +1,31 @@
// #![warn(missing_docs)] // #![warn(missing_docs)]
// #[allow(unused_extern_crates)]
pub extern crate libc; pub extern crate libc;
pub extern crate nixide_sys as sys; pub extern crate nixide_sys as sys;
pub(crate) mod errors; pub(crate) mod errors;
mod expr;
// mod flake;
mod stdext; mod stdext;
mod store;
pub(crate) mod util; pub(crate) mod util;
mod verbosity; mod verbosity;
mod version; mod version;
#[cfg(feature = "expr")]
mod expr;
#[cfg(feature = "store")]
mod store;
#[cfg(feature = "flake")]
mod flake;
pub use errors::{NixError, NixideError, NixideResult}; pub use errors::{NixError, NixideError, NixideResult};
pub use expr::{EvalState, EvalStateBuilder, Value, ValueType};
pub use store::{Store, StorePath};
pub use verbosity::NixVerbosity; pub use verbosity::NixVerbosity;
pub use version::NixVersion; pub use version::NixVersion;
#[cfg(feature = "expr")] /// Sets the verbosity level
pub use expr::{EvalState, EvalStateBuilder, Value, ValueType};
#[cfg(feature = "store")]
pub use store::{Store, StorePath};
use ctor::ctor;
use util::wrappers::AsInnerPtr as _;
pub(crate) static mut INIT_LIBUTIL_STATUS: Option<NixideResult<()>> = None;
#[cfg(feature = "store")]
pub(crate) static mut INIT_LIBSTORE_STATUS: Option<NixideResult<()>> = None;
#[cfg(feature = "expr")]
pub(crate) static mut INIT_LIBEXPR_STATUS: Option<NixideResult<()>> = None;
/// # Warning
/// ///
/// > Rust's philosophy is that nothing happens before or after main and [ctor](https://github.com/mmastrac/rust-ctor) /// # Arguments
/// > explicitly subverts that. The code that runs in `ctor` functions ///
/// > should be careful to limit itself to libc functions and code /// * `context` - additional error context, used as an output
/// > that does not rely on Rust's stdlib services. /// * `level` - verbosity level
/// > - Excerpt from the [github:mmastrac/rust-ctor README.md](https://github.com/mmastrac/rust-ctor?tab=readme-ov-file#warnings) pub fn set_verbosity() {
#[ctor] // nix_err nix_set_verbosity(nix_c_context * context, nix_verbosity level);
fn init_libutil() { // XXX: TODO: (implement Context first)
unsafe {
INIT_LIBUTIL_STATUS = Some(util::wrap::nix_fn!(|ctx: &errors::ErrorContext| unsafe {
sys::nix_libutil_init(ctx.as_ptr());
}));
}
}
/// # TODO
/// **Only run this if the "store" feature flag was enabled**
///
/// # Warning
///
/// > Rust's philosophy is that nothing happens before or after main and [ctor](https://github.com/mmastrac/rust-ctor)
/// > explicitly subverts that. The code that runs in `ctor` functions
/// > should be careful to limit itself to libc functions and code
/// > that does not rely on Rust's stdlib services.
/// > - Excerpt from the [github:mmastrac/rust-ctor README.md](https://github.com/mmastrac/rust-ctor?tab=readme-ov-file#warnings)
#[ctor]
#[cfg(feature = "store")]
fn init_libstore() {
// XXX: TODO: how do I support `sys::nix_libstore_init_no_load_config(context)`?
unsafe {
INIT_LIBSTORE_STATUS = Some(util::wrap::nix_fn!(|ctx: &errors::ErrorContext| unsafe {
sys::nix_libutil_init(ctx.as_ptr());
}));
}
}
/// # TODO
/// **Only run this if the "expr" feature flag was enabled**
//
/// # Warning
///
/// > Rust's philosophy is that nothing happens before or after main and [ctor](https://github.com/mmastrac/rust-ctor)
/// > explicitly subverts that. The code that runs in `ctor` functions
/// > should be careful to limit itself to libc functions and code
/// > that does not rely on Rust's stdlib services.
/// > - Excerpt from the [github:mmastrac/rust-ctor README.md](https://github.com/mmastrac/rust-ctor?tab=readme-ov-file#warnings)
#[ctor]
#[cfg(feature = "expr")]
fn init_libexpr() {
unsafe {
INIT_LIBEXPR_STATUS = Some(util::wrap::nix_fn!(|ctx: &errors::ErrorContext| unsafe {
sys::nix_libexpr_init(ctx.as_ptr());
}));
}
} }

View file

@ -5,31 +5,6 @@ use std::str::from_utf8;
use crate::errors::new_nixide_error; use crate::errors::new_nixide_error;
use crate::NixideResult; use crate::NixideResult;
pub trait AsCPtr<T> {
fn as_c_ptr(&self) -> NixideResult<*const T>;
fn into_c_ptr(self) -> NixideResult<*mut T>;
}
impl<T> AsCPtr<c_char> for T
where
T: AsRef<str>,
{
fn as_c_ptr(&self) -> NixideResult<*const c_char> {
match CStr::from_bytes_until_nul(self.as_ref().as_bytes()) {
Ok(s) => Ok(s.as_ptr()),
Err(_) => Err(new_nixide_error!(StringNulByte)),
}
}
fn into_c_ptr(self) -> NixideResult<*mut c_char> {
match CStr::from_bytes_until_nul(self.as_ref().as_bytes()) {
Ok(s) => Ok(s.as_ptr().cast_mut()),
Err(_) => Err(new_nixide_error!(StringNulByte)),
}
}
}
pub trait CCharPtrExt { pub trait CCharPtrExt {
fn to_utf8_string(self) -> NixideResult<String>; fn to_utf8_string(self) -> NixideResult<String>;

View file

@ -1,7 +1,4 @@
use crate::errors::ErrorContext;
use crate::sys; use crate::sys;
use crate::util::wrappers::AsInnerPtr as _;
use crate::util::{panic_issue, panic_issue_call_failed, wrap};
/// Verbosity level /// Verbosity level
/// ///
@ -31,46 +28,7 @@ impl From<sys::nix_verbosity> for NixVerbosity {
sys::nix_verbosity_NIX_LVL_CHATTY => NixVerbosity::Chatty, sys::nix_verbosity_NIX_LVL_CHATTY => NixVerbosity::Chatty,
sys::nix_verbosity_NIX_LVL_DEBUG => NixVerbosity::Debug, sys::nix_verbosity_NIX_LVL_DEBUG => NixVerbosity::Debug,
sys::nix_verbosity_NIX_LVL_VOMIT => NixVerbosity::Vomit, sys::nix_verbosity_NIX_LVL_VOMIT => NixVerbosity::Vomit,
value => panic_issue!( _ => panic!("nixide encountered unknown `nix_verbosity` value, please submit this as an issue at https://github.com/cry128/nixide"),
"nixide encountered unknown `nix_verbosity` value ({})",
value
),
} }
} }
} }
impl Into<sys::nix_verbosity> for NixVerbosity {
fn into(self) -> sys::nix_verbosity {
self as sys::nix_verbosity
}
}
/// Sets the verbosity level.
///
/// **This function should never fail!**
/// A panic would indicate a bug in nixide itself.
///
/// # Nix C++ API Internals
///
/// ```cpp
/// nix_err nix_set_verbosity(nix_c_context * context, nix_verbosity level)
/// {
/// if (context)
/// context->last_err_code = NIX_OK;
/// if (level > NIX_LVL_VOMIT || level < NIX_LVL_ERROR)
/// return nix_set_err_msg(context, NIX_ERR_UNKNOWN, "Invalid verbosity level");
/// try {
/// nix::verbosity = static_cast<nix::Verbosity>(level);
/// } catch (...) {
/// return nix_context_error(context);
/// }
/// return NIX_OK;
/// }
/// ```
///
pub fn set_verbosity(level: NixVerbosity) {
wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_set_verbosity(ctx.as_ptr(), level.into());
})
.unwrap_or_else(|err| panic_issue_call_failed!("{}", err))
}