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
use std::cmp::Ordering;
use std::ffi::{c_char, c_int, CString};
use std::ops::Deref;

use pkgcraft::pkg::ebuild::keyword;
use pkgcraft::utils::hash;

use crate::macros::*;
use crate::panic::ffi_catch_panic;
use crate::utils::boxed;

/// Opaque wrapper for pkgcraft::pkg::ebuild::keyword::Keyword.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct KeywordWrapper(keyword::Keyword);

/// C-compatible wrapper for pkgcraft::pkg::ebuild::keyword::Keyword.
#[derive(Debug, Clone)]
#[repr(C)]
pub struct Keyword {
    status: keyword::KeywordStatus,
    arch: *mut c_char,
    keyword: *mut KeywordWrapper,
}

impl From<keyword::Keyword> for Keyword {
    fn from(value: keyword::Keyword) -> Self {
        Keyword {
            status: value.status(),
            arch: try_ptr_from_str!(value.arch().as_ref()),
            keyword: boxed(KeywordWrapper(value)),
        }
    }
}

impl Deref for Keyword {
    type Target = keyword::Keyword;

    fn deref(&self) -> &Self::Target {
        let wrapper = try_ref_from_ptr!(self.keyword);
        &wrapper.0
    }
}

impl Drop for Keyword {
    fn drop(&mut self) {
        unsafe {
            drop(CString::from_raw(self.arch));
            drop(Box::from_raw(self.keyword));
        }
    }
}

/// Parse a string into a package keyword.
///
/// Returns NULL on error.
///
/// # Safety
/// The argument must be a non-null string.
#[no_mangle]
pub unsafe extern "C" fn pkgcraft_keyword_new(s: *const c_char) -> *mut Keyword {
    ffi_catch_panic! {
        let s = try_str_from_ptr!(s);
        let keyword = unwrap_or_panic!(keyword::Keyword::try_new(s));
        Box::into_raw(Box::new(keyword.into()))
    }
}

/// Compare two package keywords returning -1, 0, or 1 if the first is less than, equal to,
/// or greater than the second, respectively.
///
/// # Safety
/// The arguments must be non-null Keyword pointers.
#[no_mangle]
pub unsafe extern "C" fn pkgcraft_keyword_cmp(k1: *mut Keyword, k2: *mut Keyword) -> c_int {
    let k1 = try_ref_from_ptr!(k1);
    let k2 = try_ref_from_ptr!(k2);

    match k1.cmp(k2) {
        Ordering::Less => -1,
        Ordering::Equal => 0,
        Ordering::Greater => 1,
    }
}

/// Return the hash value for a package keyword.
///
/// # Safety
/// The argument must be a non-null Keyword pointer.
#[no_mangle]
pub unsafe extern "C" fn pkgcraft_keyword_hash(k: *mut Keyword) -> u64 {
    let keyword = try_ref_from_ptr!(k);
    hash(keyword.deref())
}

/// Return the string for a package keyword.
///
/// # Safety
/// The argument must be a non-null Keyword pointer.
#[no_mangle]
pub unsafe extern "C" fn pkgcraft_keyword_str(k: *mut Keyword) -> *mut c_char {
    let keyword = try_ref_from_ptr!(k);
    try_ptr_from_str!(keyword.to_string())
}

/// Free a package keyword.
///
/// # Safety
/// The argument must be a Keyword pointer or NULL.
#[no_mangle]
pub unsafe extern "C" fn pkgcraft_keyword_free(k: *mut Keyword) {
    if !k.is_null() {
        unsafe { drop(Box::from_raw(k)) };
    }
}