diff --git a/flake.lock b/flake.lock index b306085..b17dd36 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,26 @@ { "nodes": { + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1774682177, + "narHash": "sha256-OVbuJnJLlbHE28eRMudjtA6NXz/ifuXSho79gvh6GHY=", + "owner": "nix-community", + "repo": "fenix", + "rev": "e0f515387df77b9fdbaaf81e7f866f0365474c18", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1773222311, @@ -18,10 +39,28 @@ }, "root": { "inputs": { + "fenix": "fenix", "nixpkgs": "nixpkgs", "systems": "systems" } }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1774569884, + "narHash": "sha256-E8iWEPzg7OnE0XXXjo75CX7xFauqzJuGZ5wSO9KS8Ek=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "443ddcddd0c73b07b799d052f5ef3b448c2f3508", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + }, "systems": { "locked": { "lastModified": 1681028828, diff --git a/flake.nix b/flake.nix index f52bd4c..30f7a1d 100644 --- a/flake.nix +++ b/flake.nix @@ -4,6 +4,11 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; systems.url = "github:nix-systems/default"; + + fenix = { + url = "github:nix-community/fenix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = { @@ -29,8 +34,11 @@ pkgs = mkPkgs system nixpkgs; }); in { - overlays.default = self: super: { - libclang = super.llvmPackages_21.libclang; + overlays = { + default = self: super: { + libclang = super.llvmPackages_21.libclang; + }; + fenix = inputs.fenix.overlays.default; }; devShells = forAllSystems ( @@ -107,6 +115,91 @@ shellHook = postConfigure; + env = let + inherit (llvmPackages) llvm libclang; + in { + LD_LIBRARY_PATH = builtins.toString (lib.makeLibraryPath buildInputs); + LIBCLANG_PATH = "${libclang.lib}/lib"; + + RUST_SRC_PATH = "${pkgs.rustPlatform.rustLibSrc}"; + BINDGEN_EXTRA_CLANG_ARGS = "--sysroot=${pkgs.glibc.dev}"; + + # `cargo-llvm-cov` reads these environment variables to find these binaries, + # which are needed to run the tests + LLVM_COV = "${llvm}/bin/llvm-cov"; + LLVM_PROFDATA = "${llvm}/bin/llvm-profdata"; + }; + }; + + nightly = let + nixForBindings = pkgs.nixVersions.nix_2_32; + inherit (pkgs.rustc) llvmPackages; + in + pkgs.mkShell rec { + name = "nixide"; + shell = "${pkgs.bash}/bin/bash"; + strictDeps = true; + + # packages we need at runtime + packages = with pkgs; [ + llvmPackages.lld + lldb + (pkgs.fenix.complete.withComponents [ + "cargo" + "clippy" + "rust-src" + "rustc" + "rustfmt" + ]) + rust-analyzer-nightly + + # cargo-c + # cargo-llvm-cov + # cargo-nextest + ]; + + # packages we need at build time + nativeBuildInputs = with pkgs; [ + pkg-config + glibc.dev + nixForBindings.dev + + rustPlatform.bindgenHook + ]; + + # packages we link against + buildInputs = with pkgs; [ + stdenv.cc + + nixForBindings + ]; + + # bindgen uses clang to generate bindings, but it doesn't know where to + # find our stdenv cc's headers, so when it's gcc, we need to tell it. + postConfigure = lib.optionalString pkgs.stdenv.cc.isGNU '' + #!/usr/bin/env bash + # REF: https://github.com/nixops4/nix-bindings-rust/blob/main/bindgen-gcc.sh + # Rust bindgen uses Clang to generate bindings, but that means that it can't + # find the "system" or compiler headers when the stdenv compiler is GCC. + # This script tells it where to find them. + + echo "Extending BINDGEN_EXTRA_CLANG_ARGS with system include paths..." 2>&1 + BINDGEN_EXTRA_CLANG_ARGS="$${BINDGEN_EXTRA_CLANG_ARGS:-}" + export BINDGEN_EXTRA_CLANG_ARGS + include_paths=$( + echo | $NIX_CC_UNWRAPPED -v -E -x c - 2>&1 \ + | awk '/#include <...> search starts here:/{flag=1;next} \ + /End of search list./{flag=0} \ + flag==1 {print $1}' + ) + for path in $include_paths; do + echo " - $path" 2>&1 + BINDGEN_EXTRA_CLANG_ARGS="$BINDGEN_EXTRA_CLANG_ARGS -I$path" + done + ''; + + shellHook = postConfigure; + env = let inherit (llvmPackages) llvm libclang; in { diff --git a/nixide/src/expr/evalstate.rs b/nixide/src/expr/evalstate.rs index b73b74d..4696549 100644 --- a/nixide/src/expr/evalstate.rs +++ b/nixide/src/expr/evalstate.rs @@ -82,7 +82,7 @@ impl EvalState { ); value }) - .map(|ptr| Value::from((ptr, self))) + .map(|ptr| Value::from((ptr, std::rc::Rc::new(std::cell::RefCell::new(self))))) } /// Allocate a new value. @@ -96,7 +96,8 @@ impl EvalState { sys::nix_alloc_value(ctx.as_ptr(), self.as_ptr()) })?; - Ok(Value::from((inner, self))) + // Ok(Value::from((inner, self))) + todo!() } } diff --git a/nixide/src/expr/tests.rs b/nixide/src/expr/tests.rs index 702b44d..9d2df97 100644 --- a/nixide/src/expr/tests.rs +++ b/nixide/src/expr/tests.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use serial_test::serial; -use super::{EvalStateBuilder, ValueType}; +use super::{EvalStateBuilder, Value}; use crate::Store; #[test] @@ -29,8 +29,12 @@ fn test_simple_evaluation() { .eval_from_string("1 + 2", "") .expect("Failed to evaluate expression"); - assert_eq!(result.value_type(), ValueType::Int); - assert_eq!(result.as_int().expect("Failed to get int value"), 3); + assert!(matches!(result, Value::Int(_))); + if let Value::Int(value) = result { + assert_eq!(value.as_int(), 3); + } else { + unreachable!(); + } } #[test] @@ -46,22 +50,34 @@ fn test_value_types() { let int_val = state .eval_from_string("42", "") .expect("Failed to evaluate int"); - assert_eq!(int_val.value_type(), ValueType::Int); - assert_eq!(int_val.as_int().expect("Failed to get int"), 42); + assert!(matches!(int_val, Value::Int(_))); + if let Value::Int(value) = int_val { + assert_eq!(value.as_int(), 42); + } else { + unreachable!(); + } // Test boolean let bool_val = state .eval_from_string("true", "") .expect("Failed to evaluate bool"); - assert_eq!(bool_val.value_type(), ValueType::Bool); - assert!(bool_val.as_bool().expect("Failed to get bool")); + assert!(matches!(bool_val, Value::Bool(_))); + if let Value::Bool(value) = bool_val { + assert_eq!(value.as_bool(), true); + } else { + unreachable!(); + } // Test string - let str_val = state + let string_val = state .eval_from_string("\"hello\"", "") .expect("Failed to evaluate string"); - assert_eq!(str_val.value_type(), ValueType::String); - assert_eq!(str_val.as_string().expect("Failed to get string"), "hello"); + assert!(matches!(string_val, Value::String(_))); + if let Value::String(value) = string_val { + assert_eq!(value.as_string(), "hello"); + } else { + unreachable!(); + } } #[test] @@ -78,35 +94,29 @@ fn test_value_formatting() { .eval_from_string("42", "") .expect("Failed to evaluate int"); assert_eq!(format!("{int_val}"), "42"); - assert_eq!(format!("{int_val:?}"), "Value::Int(42)"); - assert_eq!(int_val.to_nix_string().expect("Failed to format"), "42"); + assert_eq!(format!("{int_val:?}"), "Value::Int(NixInt(42))"); // Test boolean formatting - let bool_val = state + let true_val = state .eval_from_string("true", "") .expect("Failed to evaluate bool"); - assert_eq!(format!("{bool_val}"), "true"); - assert_eq!(format!("{bool_val:?}"), "Value::Bool(true)"); - assert_eq!(bool_val.to_nix_string().expect("Failed to format"), "true"); + assert_eq!(format!("{true_val}"), "true"); + assert_eq!(format!("{true_val:?}"), "Value::Bool(NixBool(true))"); let false_val = state .eval_from_string("false", "") .expect("Failed to evaluate bool"); assert_eq!(format!("{false_val}"), "false"); - assert_eq!( - false_val.to_nix_string().expect("Failed to format"), - "false" - ); + assert_eq!(format!("{false_val:?}"), "Value::Bool(NixBool(false))"); // Test string formatting let str_val = state .eval_from_string("\"hello world\"", "") .expect("Failed to evaluate string"); assert_eq!(format!("{str_val}"), "hello world"); - assert_eq!(format!("{str_val:?}"), "Value::String(\"hello world\")"); assert_eq!( - str_val.to_nix_string().expect("Failed to format"), - "\"hello world\"" + format!("{str_val:?}"), + "Value::String(NixString(\"hello world\"))" ); // Test string with quotes @@ -115,8 +125,8 @@ fn test_value_formatting() { .expect("Failed to evaluate quoted string"); assert_eq!(format!("{quoted_str}"), "say \"hello\""); assert_eq!( - quoted_str.to_nix_string().expect("Failed to format"), - "\"say \\\"hello\\\"\"" + format!("{quoted_str:?}"), + "Value::String(NixString(say \"hello\"))" ); // Test null formatting @@ -124,8 +134,7 @@ fn test_value_formatting() { .eval_from_string("null", "") .expect("Failed to evaluate null"); assert_eq!(format!("{null_val}"), "null"); - assert_eq!(format!("{null_val:?}"), "Value::Null"); - assert_eq!(null_val.to_nix_string().expect("Failed to format"), "null"); + assert_eq!(format!("{null_val:?}"), "Value::Null(NixNull)"); // Test collection formatting let attrs_val = state diff --git a/nixide/src/expr/values/attrs.rs b/nixide/src/expr/values/attrs.rs index 43d8884..8008bf7 100644 --- a/nixide/src/expr/values/attrs.rs +++ b/nixide/src/expr/values/attrs.rs @@ -28,13 +28,13 @@ impl Drop for NixAttrs { impl Display for NixAttrs { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - write!(f, "") + write!(f, "{{ }}") } } impl Debug for NixAttrs { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - write!(f, "NixAttrs") + write!(f, "NixAttrs({{ }})") // XXX: TODO: format attrNames into here } } diff --git a/nixide/src/expr/values/bool.rs b/nixide/src/expr/values/bool.rs index 9aa17e5..d604c94 100644 --- a/nixide/src/expr/values/bool.rs +++ b/nixide/src/expr/values/bool.rs @@ -27,13 +27,13 @@ impl Drop for NixBool { impl Display for NixBool { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - write!(f, "") + write!(f, "{}", self.value()) } } impl Debug for NixBool { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - write!(f, "NixBool(${})", self.value) + write!(f, "NixBool({})", self.value) } } @@ -80,7 +80,12 @@ impl NixBool { /// Returns a shared reference to the underlying value. /// #[inline] - fn value(&self) -> &bool { + pub fn value(&self) -> &bool { &self.value } + + #[inline] + pub fn as_bool(&self) -> bool { + self.value + } } diff --git a/nixide/src/expr/values/float.rs b/nixide/src/expr/values/float.rs index 82b635a..1187821 100644 --- a/nixide/src/expr/values/float.rs +++ b/nixide/src/expr/values/float.rs @@ -26,13 +26,13 @@ impl Drop for NixFloat { impl Display for NixFloat { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - write!(f, "") + write!(f, "{}", self.value()) } } impl Debug for NixFloat { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - write!(f, "NixFloat(${})", self.value()) + write!(f, "NixFloat({})", self.value()) } } @@ -79,7 +79,12 @@ impl NixFloat { /// Returns a shared reference to the underlying value. /// #[inline] - fn value(&self) -> &f64 { + pub fn value(&self) -> &f64 { &self.value } + + #[inline] + pub fn as_float(&self) -> f64 { + self.value + } } diff --git a/nixide/src/expr/values/function.rs b/nixide/src/expr/values/function.rs index 895128f..5cf15a2 100644 --- a/nixide/src/expr/values/function.rs +++ b/nixide/src/expr/values/function.rs @@ -74,14 +74,8 @@ impl NixValue for NixFunction { } } -// impl Fn<(Value,)> for NixFunction { -// extern "rust-call" fn call(&self, args: (Value,)) -> Value { -// args.0 -// } -// } - impl NixFunction { - fn call(&self, arg: &T) -> Value + pub fn call(&self, arg: &T) -> Value where T: NixValue, { @@ -104,7 +98,7 @@ impl NixFunction { Value::from((inner, self.state.clone())) } - fn call_many(&self, args: &[&T]) -> Value + pub fn call_many(&self, args: &[&T]) -> Value where T: NixValue, { @@ -128,3 +122,13 @@ impl NixFunction { Value::from((inner, self.state.clone())) } } + +// #[cfg(nightly)] +// impl Fn<(&T,)> for NixFunction +// where +// T: NixValue, +// { +// extern "rust-call" fn call(&self, args: (&T,)) -> Value { +// self.call(args.0) +// } +// } diff --git a/nixide/src/expr/values/int.rs b/nixide/src/expr/values/int.rs index 3b381da..eccd7ef 100644 --- a/nixide/src/expr/values/int.rs +++ b/nixide/src/expr/values/int.rs @@ -26,13 +26,13 @@ impl Drop for NixInt { impl Display for NixInt { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - write!(f, "") + write!(f, "{}", self.value()) } } impl Debug for NixInt { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - write!(f, "NixInt(${})", self.value()) + write!(f, "NixInt({})", self.value()) } } @@ -77,7 +77,12 @@ impl NixInt { /// Returns a shared reference to the underlying value. /// #[inline] - fn value(&self) -> &i64 { + pub fn value(&self) -> &i64 { &self.value } + + #[inline] + pub fn as_int(&self) -> i64 { + self.value + } } diff --git a/nixide/src/expr/values/list.rs b/nixide/src/expr/values/list.rs index 96dd9f5..c062a87 100644 --- a/nixide/src/expr/values/list.rs +++ b/nixide/src/expr/values/list.rs @@ -26,13 +26,13 @@ impl Drop for NixList { impl Display for NixList { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - write!(f, "") + write!(f, "[ ]") } } impl Debug for NixList { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - write!(f, "NixList") + write!(f, "NixList([ ])") } } @@ -76,7 +76,7 @@ impl NixValue for NixList { impl NixList { /// Forces the evaluation on all elements of the list. /// - fn as_vec(&self) -> Vec { + pub fn as_vec(&self) -> Vec { // XXX: TODO: should I just return a LazyArray instead? let mut value = Vec::new(); for i in 0..self.len() { @@ -86,7 +86,7 @@ impl NixList { value } - fn as_vec_lazy(&self) -> Vec { + pub fn as_vec_lazy(&self) -> Vec { // XXX: TODO: should I just return a LazyArray instead? let mut value = Vec::new(); for i in 0..self.len() { @@ -99,14 +99,14 @@ impl NixList { /// Get the length of a list. This function preserves /// laziness and does not evaluate the internal fields. /// - fn len(&self) -> u32 { + pub fn len(&self) -> u32 { wrap::nix_fn!(|ctx: &ErrorContext| unsafe { sys::nix_get_list_size(ctx.as_ptr(), self.as_ptr()) }) .unwrap_or_else(|err| panic_issue_call_failed!("{}", err)) } - fn get(&self, index: u32) -> Value { + pub fn get(&self, index: u32) -> Value { let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { sys::nix_get_list_byidx( ctx.as_ptr(), @@ -120,7 +120,7 @@ impl NixList { Value::from((inner, self.state.clone())) } - fn get_lazy(&self, index: u32) -> NixThunk { + pub fn get_lazy(&self, index: u32) -> NixThunk { let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe { sys::nix_get_list_byidx_lazy( ctx.as_ptr(), diff --git a/nixide/src/expr/values/mod.rs b/nixide/src/expr/values/mod.rs index 7d2ff7d..27fd5f6 100644 --- a/nixide/src/expr/values/mod.rs +++ b/nixide/src/expr/values/mod.rs @@ -12,7 +12,7 @@ mod thunk; pub use attrs::NixAttrs; pub use bool::NixBool; -pub use external::NixExternal; +// pub use external::NixExternal; // pub use failed::NixFailed; // only in latest nix version pub use float::NixFloat; pub use function::NixFunction; @@ -24,7 +24,7 @@ pub use string::NixString; pub use thunk::NixThunk; use std::cell::RefCell; -use std::fmt::{Debug, Display}; +use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; use std::ptr::NonNull; use std::rc::Rc; @@ -107,12 +107,8 @@ pub enum Value { /// TODO Function(NixFunction), - - /// TODO - External(NixExternal), - - /// TODO - Failed(NixFailed), + // External(NixExternal), + // Failed(NixFailed), } impl From<(NonNull, Rc>)> for Value { @@ -142,13 +138,61 @@ impl From<(NonNull, Rc>)> for Value { ValueType_NIX_TYPE_FUNCTION => { Value::Function(::from(inner, state)) }, - ValueType_NIX_TYPE_EXTERNAL => { - Value::External(::from(inner, state)) - }, - // | sys::ValueType_NIX_TYPE_FAILED => { + // ValueType_NIX_TYPE_EXTERNAL => { + // Value::External(::from(inner, state)) + // }, + // ValueType_NIX_TYPE_FAILED => { // Value::Failed(::from(inner, state)) // }, _ => unreachable!(), } } } + +impl Display for Value { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + match self { + Value::Thunk(value) => write!(f, "{value}"), + Value::Int(value) => write!(f, "{value}"), + Value::Float(value) => write!(f, "{value}"), + Value::Bool(value) => write!(f, "{value}"), + Value::String(value) => write!(f, "{value}"), + Value::Path(value) => write!(f, "{value}"), + Value::Null(value) => write!(f, "{value}"), + Value::Attrs(value) => write!(f, "{value}"), + Value::List(value) => write!(f, "{value}"), + Value::Function(value) => write!(f, "{value}"), + // Value::External(value) => write!(f, "{value}"), + // Value::Failed(value) => write!(f, "{value}"), + } + } +} + +impl Debug for Value { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + match self { + Value::Thunk(value) => write!(f, "Value::Thunk({value:?})"), + Value::Int(value) => write!(f, "Value::Int({value:?})"), + Value::Float(value) => write!(f, "Value::Float({value:?})"), + Value::Bool(value) => write!(f, "Value::Bool({value:?})"), + Value::String(value) => write!(f, "Value::String({value:?})"), + Value::Path(value) => write!(f, "Value::Path({value:?})"), + Value::Null(value) => write!(f, "Value::Null({value:?})"), + Value::Attrs(value) => write!(f, "Value::Attrs({value:?})"), + Value::List(value) => write!(f, "Value::List({value:?})"), + Value::Function(value) => write!(f, "Value::Function({value:?})"), + // Value::External(value) => write!(f, "Value::External({value:?})"), + // Value::Failed(value) => write!(f, "Value::Failed({value:?})"), + } + } +} + +// macro_rules! is_type { +// ($expr:expr, $tt:tt) => {{ +// match $expr { +// $tt => true, +// _ => false, +// } +// }}; +// } +// pub(self) use is_type; diff --git a/nixide/src/expr/values/path.rs b/nixide/src/expr/values/path.rs index 145b2d2..9a7fdb2 100644 --- a/nixide/src/expr/values/path.rs +++ b/nixide/src/expr/values/path.rs @@ -29,13 +29,13 @@ impl Drop for NixPath { impl Display for NixPath { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - write!(f, "") + write!(f, "{}", self.value.display()) } } impl Debug for NixPath { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - write!(f, "NixPath(\"${}\")", self.value().display()) + write!(f, "NixPath(\"{}\")", self.value().display()) } } @@ -82,7 +82,12 @@ impl NixPath { /// Returns a shared reference to the underlying value. /// #[inline] - fn value(&self) -> &PathBuf { + pub fn value(&self) -> &PathBuf { &self.value } + + #[inline] + pub fn as_path(&self) -> PathBuf { + self.value.clone() + } } diff --git a/nixide/src/expr/values/string.rs b/nixide/src/expr/values/string.rs index 2ceeb41..9ee305a 100644 --- a/nixide/src/expr/values/string.rs +++ b/nixide/src/expr/values/string.rs @@ -28,13 +28,13 @@ impl Drop for NixString { impl Display for NixString { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - write!(f, "") + write!(f, "{}", self.value()) } } impl Debug for NixString { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - write!(f, "NixString(\"${}\")", self.value()) + write!(f, "NixString(\"{}\")", self.value()) } } @@ -86,7 +86,12 @@ impl NixString { /// Returns a shared reference to the underlying value. /// #[inline] - fn value(&self) -> &String { + pub fn value(&self) -> &String { &self.value } + + #[inline] + pub fn as_string(&self) -> String { + self.value.clone() + } } diff --git a/nixide/src/expr/values/thunk.rs b/nixide/src/expr/values/thunk.rs index 19a5470..5fbe0f7 100644 --- a/nixide/src/expr/values/thunk.rs +++ b/nixide/src/expr/values/thunk.rs @@ -65,11 +65,11 @@ impl NixValue for NixThunk { } impl NixThunk { - fn to_inner(self) -> NonNull { - self.inner - } + // fn to_inner(self) -> NonNull { + // self.inner + // } - fn eval(self) -> Value { + pub fn eval(self) -> Value { wrap::nix_fn!(|ctx: &ErrorContext| unsafe { sys::nix_value_force( ctx.as_ptr(), diff --git a/nixide/src/lib.rs b/nixide/src/lib.rs index 4601253..ccbccf1 100644 --- a/nixide/src/lib.rs +++ b/nixide/src/lib.rs @@ -1,6 +1,7 @@ // #![warn(missing_docs)] #![cfg_attr(nightly, feature(fn_traits))] +#![cfg_attr(nightly, feature(unboxed_closures))] pub extern crate libc; pub extern crate nixide_sys as sys;