1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
use crate::{
    errors::{ReaperError, ReaperResult},
    reaper_pointer::ReaperPointer,
    Project, Reaper,
};
use std::{
    ffi::{c_char, CStr, CString},
    str::Utf8Error,
};

/// Returns self as a null-terminated String. Implemented only for [String].
pub trait WithNull: Clone {
    /// If not `\0` at the end, it will be added.
    fn with_null(&mut self) -> &String;
}
impl WithNull for String {
    fn with_null(&mut self) -> &String {
        if !self.ends_with("\0") {
            self.push('\0');
        }
        self
    }
}

/// Convert string to CString pointer for using with low-level.
pub fn as_mut_i8<'a>(value: impl Into<&'a str>) -> *mut i8 {
    let value: &str = value.into();
    let vec: Vec<u8> = value.chars().map(|val| val as u8).collect();
    let string: CString = unsafe { CString::from_vec_unchecked(vec) };
    string.into_raw()
}

/// Convert string to CStr pointer for using with low-level.
pub fn as_c_char<'a>(value: impl Into<&'a str>) -> *const c_char {
    let value = String::from(value.into());
    let value = value + "\0";
    let value = value.as_str();
    let value = CStr::from_bytes_with_nul(value.as_bytes()).unwrap();
    value.as_ptr()
}

/// Has hot to contain Null Byte!!!
pub fn as_c_string<'a>(value: &'a String) -> CString {
    let value = CString::new(value.as_bytes()).unwrap();
    value
}

/// Convert null-terminated String to CStr.
///
/// You can use trait [WithNull].
pub fn as_c_str<'a>(value: &'a String) -> &'a CStr {
    let value = CStr::from_bytes_with_nul(value.as_bytes()).unwrap();
    value
}

/// Convert pointer to CStr to String.
pub fn as_string(ptr: *const i8) -> Result<String, Utf8Error> {
    let value: &CStr = unsafe { CStr::from_ptr(ptr) };
    let value = value.to_str()?;
    let value = String::from(value);
    Ok(value)
}

/// Convert pointer to CString to String.
pub fn as_string_mut(ptr: *mut i8) -> Result<String, Utf8Error> {
    unsafe { Ok(String::from(CString::from_raw(ptr).to_str()?)) }
}

/// Make empty CString pointer of the given size.
pub fn make_string_buf(size: usize) -> *mut i8 {
    unsafe {
        let buf: Vec<u8> = vec![0; size];
        CString::from_vec_unchecked(buf).into_raw()
    }
}

/// Make empty CString of the given size.
pub fn make_c_string_buf(size: usize) -> CString {
    unsafe {
        let buf: Vec<u8> = vec![0; size];
        CString::from_vec_unchecked(buf)
    }
}

/// Guarantees that REAPER object has valid pointer.
///
/// Gives the API user as much control, as he wishes.
///
/// By default, implementation has to check validity
/// with every access to the pointer e.g. with every
/// method call. But the amount of checks can be reduced
/// by the [WithReaperPtr::with_valid_ptr()] method,
/// or by manually turning validation checks off and on
/// by [WithReaperPtr::make_unchecked] and
/// [WithReaperPtr::make_checked] respectively.
///
/// # Implementation
///
/// - `get_pointer` should return raw NonNull unchecked
/// ReaperPointer.
/// - After invocation of `make_unchecked`, method `should_check`
/// has to return `false`.
/// - After invocation of `make_checked`, method `should_check`
/// has to return `true`.
/// - `get()` call should invoke either `require_valid`
/// or `require_valid_2`.
pub trait WithReaperPtr {
    type Ptr: Into<ReaperPointer>;
    /// Get underlying ReaperPointer.
    fn get_pointer(&self) -> Self::Ptr;
    /// Get underlying ReaperPointer with validity check.
    fn get(&self) -> Self::Ptr;
    /// Turn validity checks off.
    fn make_unchecked(&mut self);
    /// Turn validity checks on.
    fn make_checked(&mut self);
    /// State of validity checks.
    fn should_check(&self) -> bool;

    /// Return [ReaperError::NullPtr] if check failed.
    ///
    /// # Note
    ///
    /// Will not check if turned off by
    /// [`WithReaperPtr::make_unchecked`].
    fn require_valid(&self) -> ReaperResult<()> {
        if !self.should_check() {
            return Ok(());
        }
        let ptr = self.get_pointer();
        match Reaper::get().validate_ptr(ptr) {
            true => Ok(()),
            false => Err(Box::new(ReaperError::NullPtr)),
        }
    }

    /// Return [ReaperError::NullPtr] if check failed.
    ///
    /// # Note
    ///
    /// Will not check if turned off by
    /// [`WithReaperPtr::make_unchecked`].
    fn require_valid_2(&self, project: &Project) -> ReaperResult<()> {
        if !self.should_check() {
            return Ok(());
        }
        let ptr = self.get_pointer();
        match Reaper::get().validate_ptr_2(project, ptr) {
            true => Ok(()),
            false => Err(Box::new(ReaperError::NullPtr)),
        }
    }

    /// Perform function with only one validity check.
    ///
    /// Returns [ReaperError::NullPtr] if the first check
    /// failed. Also propagates any error returned from
    /// function.
    fn with_valid_ptr(
        &mut self,
        mut f: impl FnMut(&mut Self) -> ReaperResult<()>,
    ) -> ReaperResult<()> {
        self.require_valid()?;
        self.make_unchecked();
        (f)(self)?;
        self.make_checked();
        Ok(())
    }
}