1use std::borrow::Borrow;
4use std::ffi::{CString, c_char};
5use std::fmt::{Debug, Formatter};
6
7use crate::context::LlamaContext;
8use crate::ffi_error_reader::read_and_free_cpp_error;
9use crate::model::LlamaModel;
10use crate::token::LlamaToken;
11use crate::token::data_array::LlamaTokenDataArray;
12use crate::token::logit_bias::LlamaLogitBias;
13use crate::{GrammarError, SampleError, SamplerAcceptError, SamplingError};
14
15fn check_sampler_accept_status(
16 status: llama_cpp_bindings_sys::llama_rs_status,
17 error_ptr: *mut c_char,
18) -> Result<(), SamplerAcceptError> {
19 match status {
20 llama_cpp_bindings_sys::LLAMA_RS_STATUS_OK => Ok(()),
21 llama_cpp_bindings_sys::LLAMA_RS_STATUS_INVALID_ARGUMENT => {
22 Err(SamplerAcceptError::InvalidArgument)
23 }
24 _ => Err(SamplerAcceptError::CppException(unsafe {
25 read_and_free_cpp_error(error_ptr)
26 })),
27 }
28}
29
30fn check_sampler_not_null(
31 sampler: *mut llama_cpp_bindings_sys::llama_sampler,
32 error_ptr: *mut c_char,
33) -> Result<LlamaSampler, GrammarError> {
34 if sampler.is_null() {
35 Err(GrammarError::NullGrammar(unsafe {
36 read_and_free_cpp_error(error_ptr)
37 }))
38 } else {
39 Ok(LlamaSampler { sampler })
40 }
41}
42
43fn checked_u32_as_i32(value: u32) -> Result<i32, GrammarError> {
44 i32::try_from(value).map_err(|convert_error| {
45 GrammarError::IntegerOverflow(format!("value exceeds i32::MAX: {convert_error}"))
46 })
47}
48
49fn checked_usize_as_i32_sampling(value: usize) -> Result<i32, SamplingError> {
50 i32::try_from(value).map_err(|convert_error| {
51 SamplingError::IntegerOverflow(format!("value exceeds i32::MAX: {convert_error}"))
52 })
53}
54
55pub struct LlamaSampler {
57 pub sampler: *mut llama_cpp_bindings_sys::llama_sampler,
59}
60
61impl Debug for LlamaSampler {
62 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
63 f.debug_struct("LlamaSamplerChain").finish()
64 }
65}
66
67impl LlamaSampler {
68 pub fn sample(&mut self, ctx: &LlamaContext, idx: i32) -> Result<LlamaToken, SampleError> {
74 let mut token: i32 = -1;
75 let mut error_ptr: *mut c_char = std::ptr::null_mut();
76
77 let status = unsafe {
78 llama_cpp_bindings_sys::llama_rs_sampler_sample(
79 self.sampler,
80 ctx.context.as_ptr(),
81 idx,
82 &raw mut token,
83 &raw mut error_ptr,
84 )
85 };
86
87 match status {
88 llama_cpp_bindings_sys::LLAMA_RS_STATUS_OK => Ok(LlamaToken(token)),
89 llama_cpp_bindings_sys::LLAMA_RS_STATUS_INVALID_ARGUMENT => {
90 Err(SampleError::InvalidArgument)
91 }
92 _ => Err(SampleError::CppException(unsafe {
93 read_and_free_cpp_error(error_ptr)
94 })),
95 }
96 }
97
98 pub fn apply(&self, data_array: &mut LlamaTokenDataArray) {
100 data_array.apply_sampler(self);
101 }
102
103 pub fn accept(&mut self, token: LlamaToken) -> Result<(), SamplerAcceptError> {
109 self.try_accept(token)
110 }
111
112 pub fn accept_many(
118 &mut self,
119 tokens: impl IntoIterator<Item = impl Borrow<LlamaToken>>,
120 ) -> Result<(), SamplerAcceptError> {
121 for token in tokens {
122 self.try_accept(*token.borrow())?;
123 }
124
125 Ok(())
126 }
127
128 pub fn with_tokens(
134 mut self,
135 tokens: impl IntoIterator<Item = impl Borrow<LlamaToken>>,
136 ) -> Result<Self, SamplerAcceptError> {
137 self.accept_many(tokens)?;
138
139 Ok(self)
140 }
141
142 pub fn try_accept(&mut self, token: LlamaToken) -> Result<(), SamplerAcceptError> {
147 let mut error_ptr: *mut c_char = std::ptr::null_mut();
148
149 let status = unsafe {
150 llama_cpp_bindings_sys::llama_rs_sampler_accept(
151 self.sampler,
152 token.0,
153 &raw mut error_ptr,
154 )
155 };
156
157 check_sampler_accept_status(status, error_ptr)
158 }
159
160 pub fn reset(&mut self) {
164 unsafe {
165 llama_cpp_bindings_sys::llama_sampler_reset(self.sampler);
166 }
167 }
168
169 #[must_use]
176 pub fn get_seed(&self) -> u32 {
177 unsafe { llama_cpp_bindings_sys::llama_sampler_get_seed(self.sampler) }
178 }
179
180 #[must_use]
187 pub fn chain(samplers: impl IntoIterator<Item = Self>, no_perf: bool) -> Self {
188 unsafe {
189 let chain = llama_cpp_bindings_sys::llama_sampler_chain_init(
190 llama_cpp_bindings_sys::llama_sampler_chain_params { no_perf },
191 );
192
193 for sampler in samplers {
194 llama_cpp_bindings_sys::llama_sampler_chain_add(chain, sampler.sampler);
195 std::mem::forget(sampler);
196 }
197
198 Self { sampler: chain }
199 }
200 }
201
202 #[must_use]
234 pub fn chain_simple(samplers: impl IntoIterator<Item = Self>) -> Self {
235 Self::chain(samplers, false)
236 }
237
238 #[must_use]
263 pub fn temp(t: f32) -> Self {
264 let sampler = unsafe { llama_cpp_bindings_sys::llama_sampler_init_temp(t) };
265 Self { sampler }
266 }
267
268 #[must_use]
271 pub fn temp_ext(t: f32, delta: f32, exponent: f32) -> Self {
272 let sampler =
273 unsafe { llama_cpp_bindings_sys::llama_sampler_init_temp_ext(t, delta, exponent) };
274 Self { sampler }
275 }
276
277 #[must_use]
303 pub fn top_k(k: i32) -> Self {
304 let sampler = unsafe { llama_cpp_bindings_sys::llama_sampler_init_top_k(k) };
305 Self { sampler }
306 }
307
308 #[must_use]
334 pub fn top_n_sigma(n: f32) -> Self {
335 let sampler = unsafe { llama_cpp_bindings_sys::llama_sampler_init_top_n_sigma(n) };
336 Self { sampler }
337 }
338
339 #[must_use]
341 pub fn typical(p: f32, min_keep: usize) -> Self {
342 let sampler = unsafe { llama_cpp_bindings_sys::llama_sampler_init_typical(p, min_keep) };
343 Self { sampler }
344 }
345
346 #[must_use]
349 pub fn top_p(p: f32, min_keep: usize) -> Self {
350 let sampler = unsafe { llama_cpp_bindings_sys::llama_sampler_init_top_p(p, min_keep) };
351 Self { sampler }
352 }
353
354 #[must_use]
356 pub fn min_p(p: f32, min_keep: usize) -> Self {
357 let sampler = unsafe { llama_cpp_bindings_sys::llama_sampler_init_min_p(p, min_keep) };
358 Self { sampler }
359 }
360
361 #[must_use]
363 pub fn xtc(p: f32, t: f32, min_keep: usize, seed: u32) -> Self {
364 let sampler =
365 unsafe { llama_cpp_bindings_sys::llama_sampler_init_xtc(p, t, min_keep, seed) };
366 Self { sampler }
367 }
368
369 pub fn grammar(
374 model: &LlamaModel,
375 grammar_str: &str,
376 grammar_root: &str,
377 ) -> Result<Self, GrammarError> {
378 let (grammar_str, grammar_root) =
379 Self::sanitize_grammar_strings(grammar_str, grammar_root)?;
380 let mut error_ptr: *mut c_char = std::ptr::null_mut();
381
382 let sampler = unsafe {
383 llama_cpp_bindings_sys::llama_rs_sampler_init_grammar(
384 model.vocab_ptr(),
385 grammar_str.as_ptr(),
386 grammar_root.as_ptr(),
387 &raw mut error_ptr,
388 )
389 };
390
391 check_sampler_not_null(sampler, error_ptr)
392 }
393
394 pub fn grammar_lazy(
401 model: &LlamaModel,
402 grammar_str: &str,
403 grammar_root: &str,
404 trigger_words: impl IntoIterator<Item = impl AsRef<[u8]>>,
405 trigger_tokens: &[LlamaToken],
406 ) -> Result<Self, GrammarError> {
407 let (grammar_str, grammar_root) =
408 Self::sanitize_grammar_strings(grammar_str, grammar_root)?;
409 let trigger_words = Self::sanitize_trigger_words(trigger_words)?;
410 let mut error_ptr: *mut c_char = std::ptr::null_mut();
411
412 let mut trigger_word_ptrs: Vec<*const c_char> =
413 trigger_words.iter().map(|cs| cs.as_ptr()).collect();
414
415 let sampler = unsafe {
416 llama_cpp_bindings_sys::llama_rs_sampler_init_grammar_lazy(
417 model.vocab_ptr(),
418 grammar_str.as_ptr(),
419 grammar_root.as_ptr(),
420 trigger_word_ptrs.as_mut_ptr(),
421 trigger_word_ptrs.len(),
422 trigger_tokens.as_ptr().cast(),
423 trigger_tokens.len(),
424 &raw mut error_ptr,
425 )
426 };
427
428 check_sampler_not_null(sampler, error_ptr)
429 }
430
431 pub fn grammar_lazy_patterns(
440 model: &LlamaModel,
441 grammar_str: &str,
442 grammar_root: &str,
443 trigger_patterns: &[String],
444 trigger_tokens: &[LlamaToken],
445 ) -> Result<Self, GrammarError> {
446 let (grammar_str, grammar_root) =
447 Self::sanitize_grammar_strings(grammar_str, grammar_root)?;
448 let trigger_patterns = Self::sanitize_trigger_patterns(trigger_patterns)?;
449 let mut error_ptr: *mut c_char = std::ptr::null_mut();
450
451 let mut trigger_pattern_ptrs: Vec<*const c_char> =
452 trigger_patterns.iter().map(|cs| cs.as_ptr()).collect();
453
454 let sampler = unsafe {
455 llama_cpp_bindings_sys::llama_rs_sampler_init_grammar_lazy_patterns(
456 model.vocab_ptr(),
457 grammar_str.as_ptr(),
458 grammar_root.as_ptr(),
459 trigger_pattern_ptrs.as_mut_ptr(),
460 trigger_pattern_ptrs.len(),
461 trigger_tokens.as_ptr().cast(),
462 trigger_tokens.len(),
463 &raw mut error_ptr,
464 )
465 };
466
467 check_sampler_not_null(sampler, error_ptr)
468 }
469
470 pub fn llguidance(
479 model: &LlamaModel,
480 grammar_kind: &str,
481 grammar_data: &str,
482 ) -> Result<Self, GrammarError> {
483 crate::llguidance_sampler::create_llg_sampler(model, grammar_kind, grammar_data)
484 }
485
486 fn sanitize_grammar_strings(
487 grammar_str: &str,
488 grammar_root: &str,
489 ) -> Result<(CString, CString), GrammarError> {
490 if !grammar_str.contains(grammar_root) {
491 return Err(GrammarError::RootNotFound);
492 }
493
494 let grammar = CString::new(grammar_str).map_err(GrammarError::GrammarNullBytes)?;
495 let root = CString::new(grammar_root).map_err(GrammarError::GrammarNullBytes)?;
496
497 Ok((grammar, root))
498 }
499
500 fn sanitize_trigger_words(
501 trigger_words: impl IntoIterator<Item = impl AsRef<[u8]>>,
502 ) -> Result<Vec<CString>, GrammarError> {
503 trigger_words
504 .into_iter()
505 .map(|word| CString::new(word.as_ref()).map_err(GrammarError::TriggerWordNullBytes))
506 .collect()
507 }
508
509 fn sanitize_trigger_patterns(
510 trigger_patterns: &[String],
511 ) -> Result<Vec<CString>, GrammarError> {
512 trigger_patterns
513 .iter()
514 .map(|pattern| CString::new(pattern.as_str()).map_err(GrammarError::GrammarNullBytes))
515 .collect()
516 }
517
518 pub fn dry(
525 model: &LlamaModel,
526 multiplier: f32,
527 base: f32,
528 allowed_length: i32,
529 penalty_last_n: i32,
530 seq_breakers: impl IntoIterator<Item = impl AsRef<[u8]>>,
531 ) -> Result<Self, GrammarError> {
532 let seq_breakers: Vec<CString> = seq_breakers
533 .into_iter()
534 .map(|seq_breaker| CString::new(seq_breaker.as_ref()))
535 .collect::<Result<Vec<_>, _>>()?;
536 let mut seq_breaker_pointers: Vec<*const c_char> = seq_breakers
537 .iter()
538 .map(|seq_breaker| seq_breaker.as_ptr())
539 .collect();
540
541 let n_ctx_train_value = model.n_ctx_train().map_err(|convert_error| {
542 GrammarError::IntegerOverflow(format!(
543 "n_ctx_train does not fit into u32: {convert_error}"
544 ))
545 })?;
546 let n_ctx_train = checked_u32_as_i32(n_ctx_train_value)?;
547 let sampler = unsafe {
548 llama_cpp_bindings_sys::llama_sampler_init_dry(
549 model.vocab_ptr(),
550 n_ctx_train,
551 multiplier,
552 base,
553 allowed_length,
554 penalty_last_n,
555 seq_breaker_pointers.as_mut_ptr(),
556 seq_breaker_pointers.len(),
557 )
558 };
559
560 Ok(Self { sampler })
561 }
562
563 #[must_use]
571 pub fn penalties(
572 penalty_last_n: i32,
573 penalty_repeat: f32,
574 penalty_freq: f32,
575 penalty_present: f32,
576 ) -> Self {
577 let sampler = unsafe {
578 llama_cpp_bindings_sys::llama_sampler_init_penalties(
579 penalty_last_n,
580 penalty_repeat,
581 penalty_freq,
582 penalty_present,
583 )
584 };
585 Self { sampler }
586 }
587
588 #[must_use]
604 pub fn mirostat(n_vocab: i32, seed: u32, tau: f32, eta: f32, m: i32) -> Self {
605 let sampler = unsafe {
606 llama_cpp_bindings_sys::llama_sampler_init_mirostat(n_vocab, seed, tau, eta, m)
607 };
608 Self { sampler }
609 }
610
611 #[must_use]
622 pub fn mirostat_v2(seed: u32, tau: f32, eta: f32) -> Self {
623 let sampler =
624 unsafe { llama_cpp_bindings_sys::llama_sampler_init_mirostat_v2(seed, tau, eta) };
625 Self { sampler }
626 }
627
628 #[must_use]
630 pub fn dist(seed: u32) -> Self {
631 let sampler = unsafe { llama_cpp_bindings_sys::llama_sampler_init_dist(seed) };
632 Self { sampler }
633 }
634
635 #[must_use]
657 pub fn greedy() -> Self {
658 let sampler = unsafe { llama_cpp_bindings_sys::llama_sampler_init_greedy() };
659 Self { sampler }
660 }
661
662 pub fn logit_bias(n_vocab: i32, biases: &[LlamaLogitBias]) -> Result<Self, SamplingError> {
685 let bias_count = checked_usize_as_i32_sampling(biases.len())?;
686 let data = biases
687 .as_ptr()
688 .cast::<llama_cpp_bindings_sys::llama_logit_bias>();
689
690 let sampler = unsafe {
691 llama_cpp_bindings_sys::llama_sampler_init_logit_bias(n_vocab, bias_count, data)
692 };
693
694 Ok(Self { sampler })
695 }
696}
697
698impl Drop for LlamaSampler {
699 fn drop(&mut self) {
700 unsafe {
701 llama_cpp_bindings_sys::llama_sampler_free(self.sampler);
702 }
703 }
704}
705
706#[cfg(test)]
707mod tests {
708 use super::LlamaSampler;
709 use crate::GrammarError;
710
711 #[test]
712 fn sanitize_grammar_strings_valid() {
713 let result = LlamaSampler::sanitize_grammar_strings("root ::= \"hello\"", "root");
714
715 assert!(result.is_ok());
716 }
717
718 #[test]
719 fn sanitize_grammar_strings_root_not_found() {
720 let result = LlamaSampler::sanitize_grammar_strings("expr ::= \"hello\"", "root");
721
722 assert_eq!(result.err(), Some(GrammarError::RootNotFound));
723 }
724
725 #[test]
726 fn sanitize_grammar_strings_null_byte_in_grammar() {
727 let result = LlamaSampler::sanitize_grammar_strings("root ::= \"\0\"", "root");
728
729 assert!(matches!(
730 result.err(),
731 Some(GrammarError::GrammarNullBytes(_))
732 ));
733 }
734
735 #[test]
736 fn sanitize_grammar_strings_null_byte_in_root() {
737 let result = LlamaSampler::sanitize_grammar_strings("ro\0ot ::= \"hello\"", "ro\0ot");
738
739 assert!(matches!(
740 result.err(),
741 Some(GrammarError::GrammarNullBytes(_))
742 ));
743 }
744
745 #[test]
746 fn sanitize_trigger_words_valid() {
747 let words: Vec<&[u8]> = vec![b"hello", b"world"];
748 let result = LlamaSampler::sanitize_trigger_words(words);
749
750 assert!(result.is_ok());
751 assert_eq!(result.expect("valid trigger words").len(), 2);
752 }
753
754 #[test]
755 fn sanitize_trigger_words_empty_list() {
756 let words: Vec<&[u8]> = vec![];
757 let result = LlamaSampler::sanitize_trigger_words(words);
758
759 assert!(result.is_ok());
760 assert!(result.expect("valid trigger words").is_empty());
761 }
762
763 #[test]
764 fn sanitize_trigger_words_null_byte() {
765 let words: Vec<&[u8]> = vec![b"hel\0lo"];
766 let result = LlamaSampler::sanitize_trigger_words(words);
767
768 assert!(matches!(
769 result.err(),
770 Some(GrammarError::TriggerWordNullBytes(_))
771 ));
772 }
773
774 #[test]
775 fn sanitize_trigger_patterns_valid() {
776 let patterns = vec!["^hello$".to_string(), "world.*".to_string()];
777 let result = LlamaSampler::sanitize_trigger_patterns(&patterns);
778
779 assert!(result.is_ok());
780 assert_eq!(result.expect("valid trigger patterns").len(), 2);
781 }
782
783 #[test]
784 fn sanitize_trigger_patterns_empty_list() {
785 let patterns: Vec<String> = vec![];
786 let result = LlamaSampler::sanitize_trigger_patterns(&patterns);
787
788 assert!(result.is_ok());
789 assert!(result.expect("valid trigger patterns").is_empty());
790 }
791
792 #[test]
793 fn sanitize_trigger_patterns_null_byte() {
794 let patterns = vec!["hel\0lo".to_string()];
795 let result = LlamaSampler::sanitize_trigger_patterns(&patterns);
796
797 assert!(matches!(
798 result.err(),
799 Some(GrammarError::GrammarNullBytes(_))
800 ));
801 }
802
803 #[test]
804 fn apply_modifies_data_array() {
805 use crate::token::LlamaToken;
806 use crate::token::data::LlamaTokenData;
807 use crate::token::data_array::LlamaTokenDataArray;
808
809 let sampler = LlamaSampler::greedy();
810 let mut data_array = LlamaTokenDataArray::new(
811 vec![
812 LlamaTokenData::new(LlamaToken::new(0), 1.0, 0.0),
813 LlamaTokenData::new(LlamaToken::new(1), 5.0, 0.0),
814 ],
815 false,
816 );
817
818 sampler.apply(&mut data_array);
819
820 assert_eq!(data_array.selected_token(), Some(LlamaToken::new(1)));
821 }
822
823 #[test]
824 fn accept_succeeds() {
825 let mut sampler = LlamaSampler::chain_simple([
826 LlamaSampler::penalties(64, 1.1, 0.0, 0.0),
827 LlamaSampler::greedy(),
828 ]);
829
830 sampler
831 .accept(crate::token::LlamaToken::new(1))
832 .expect("test: accept should succeed");
833 }
834
835 #[test]
836 fn try_accept_succeeds_on_penalties_sampler() {
837 let mut sampler = LlamaSampler::chain_simple([
838 LlamaSampler::penalties(64, 1.1, 0.0, 0.0),
839 LlamaSampler::greedy(),
840 ]);
841
842 let result = sampler.try_accept(crate::token::LlamaToken::new(42));
843
844 assert!(result.is_ok());
845 }
846
847 #[test]
848 fn accept_many_multiple_tokens() {
849 use crate::token::LlamaToken;
850
851 let mut sampler = LlamaSampler::chain_simple([
852 LlamaSampler::penalties(64, 1.1, 0.0, 0.0),
853 LlamaSampler::greedy(),
854 ]);
855
856 sampler
857 .accept_many([LlamaToken::new(1), LlamaToken::new(2), LlamaToken::new(3)])
858 .expect("test: accept_many should succeed");
859 }
860
861 #[test]
862 fn with_tokens_builder_pattern() {
863 use crate::token::LlamaToken;
864
865 let _sampler = LlamaSampler::chain_simple([
866 LlamaSampler::penalties(64, 1.1, 0.0, 0.0),
867 LlamaSampler::greedy(),
868 ])
869 .with_tokens([LlamaToken::new(10), LlamaToken::new(20)])
870 .expect("test: with_tokens should succeed");
871 }
872
873 #[test]
874 fn all_sampler_constructors() {
875 use crate::token::LlamaToken;
876 use crate::token::logit_bias::LlamaLogitBias;
877
878 let _temp = LlamaSampler::temp(0.8);
879 let _temp_ext = LlamaSampler::temp_ext(0.8, 0.1, 1.0);
880 let _top_k = LlamaSampler::top_k(40);
881 let _top_n_sigma = LlamaSampler::top_n_sigma(2.0);
882 let _top_p = LlamaSampler::top_p(0.9, 1);
883 let _min_p = LlamaSampler::min_p(0.05, 1);
884 let _typical = LlamaSampler::typical(0.9, 1);
885 let _xtc = LlamaSampler::xtc(0.1, 0.5, 1, 42);
886 let _dist = LlamaSampler::dist(42);
887 let _mirostat = LlamaSampler::mirostat(32000, 42, 5.0, 0.1, 100);
888 let _mirostat_v2 = LlamaSampler::mirostat_v2(42, 5.0, 0.1);
889 let biases = vec![LlamaLogitBias::new(LlamaToken::new(0), -100.0)];
890 let _logit_bias = LlamaSampler::logit_bias(32000, &biases);
891 let _chain = LlamaSampler::chain([LlamaSampler::greedy()], true);
892 }
893
894 #[test]
895 fn reset_and_get_seed() {
896 let mut sampler = LlamaSampler::dist(42);
897 sampler.reset();
898 let _seed = sampler.get_seed();
899 }
900
901 #[test]
902 fn debug_formatting() {
903 let sampler = LlamaSampler::greedy();
904 let debug_output = format!("{sampler:?}");
905 assert!(debug_output.contains("LlamaSampler"));
906 }
907
908 #[test]
909 fn checked_u32_as_i32_overflow() {
910 let result = super::checked_u32_as_i32(u32::MAX);
911 assert!(result.is_err());
912 }
913
914 #[test]
915 fn checked_usize_as_i32_sampling_overflow() {
916 let result = super::checked_usize_as_i32_sampling(usize::MAX);
917 assert!(result.is_err());
918 }
919
920 #[test]
921 fn check_sampler_accept_status_ok() {
922 let result = super::check_sampler_accept_status(
923 llama_cpp_bindings_sys::LLAMA_RS_STATUS_OK,
924 std::ptr::null_mut(),
925 );
926
927 assert!(result.is_ok());
928 }
929
930 #[test]
931 fn check_sampler_accept_status_invalid_argument() {
932 let result = super::check_sampler_accept_status(
933 llama_cpp_bindings_sys::LLAMA_RS_STATUS_INVALID_ARGUMENT,
934 std::ptr::null_mut(),
935 );
936
937 assert!(matches!(
938 result,
939 Err(crate::SamplerAcceptError::InvalidArgument)
940 ));
941 }
942
943 #[test]
944 fn check_sampler_accept_status_exception() {
945 let result = super::check_sampler_accept_status(
946 llama_cpp_bindings_sys::LLAMA_RS_STATUS_EXCEPTION,
947 std::ptr::null_mut(),
948 );
949
950 assert!(matches!(
951 result,
952 Err(crate::SamplerAcceptError::CppException(_))
953 ));
954 }
955
956 #[test]
957 fn check_sampler_not_null_returns_error() {
958 let result = super::check_sampler_not_null(std::ptr::null_mut(), std::ptr::null_mut());
959
960 assert!(result.is_err());
961 }
962}