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
use std::ffi::{c_char, c_int};

use pkgcraft::config::{Config, Repos};
use pkgcraft::repo::set::RepoSet;
use pkgcraft::repo::Repo;

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

/// Create an empty pkgcraft config.
#[no_mangle]
pub extern "C" fn pkgcraft_config_new() -> *mut Config {
    let config = Config::new("pkgcraft", "");
    Box::into_raw(Box::new(config))
}

/// Add local repo from filesystem path.
///
/// Returns NULL on error.
///
/// # Safety
/// The path argument should be a valid path on the system.
#[no_mangle]
pub unsafe extern "C" fn pkgcraft_config_add_repo_path(
    c: *mut Config,
    id: *const c_char,
    priority: c_int,
    path: *const c_char,
    external: bool,
) -> *mut Repo {
    ffi_catch_panic! {
        let path = try_str_from_ptr!(path);
        let id = if id.is_null() {
            path
        } else {
            try_str_from_ptr!(id)
        };

        let config = try_mut_from_ptr!(c);
        let repo = unwrap_or_panic!(config.add_repo_path(id, priority, path, external));
        Box::into_raw(Box::new(repo))
    }
}

/// Add an external Repo to the config.
///
/// Returns NULL on error.
///
/// # Safety
/// The arguments must be valid Config and Repo pointers.
#[no_mangle]
pub unsafe extern "C" fn pkgcraft_config_add_repo(
    c: *mut Config,
    r: *mut Repo,
    external: bool,
) -> *mut Repo {
    ffi_catch_panic! {
        let config = try_mut_from_ptr!(c);
        let repo = try_ref_from_ptr!(r);
        unwrap_or_panic!(config.add_repo(repo, external));
        r
    }
}

/// Load the system config.
///
/// Returns NULL on error.
///
/// # Safety
/// A valid pkgcraft (or portage config) directory should be located on the system.
#[no_mangle]
pub unsafe extern "C" fn pkgcraft_config_load(c: *mut Config) -> *mut Config {
    ffi_catch_panic! {
        let config = try_mut_from_ptr!(c);
        unwrap_or_panic!(config.load());
        c
    }
}

/// Load the portage config from a given path, use NULL for the default system paths.
///
/// Returns NULL on error.
///
/// # Safety
/// The path argument should be a valid path on the system.
#[no_mangle]
pub unsafe extern "C" fn pkgcraft_config_load_portage_conf(
    c: *mut Config,
    path: *const c_char,
) -> *mut Config {
    ffi_catch_panic! {
        let path = try_opt_str_from_ptr!(path);
        let config = try_mut_from_ptr!(c);
        unwrap_or_panic!(config.load_portage_conf(path));
        c
    }
}

/// Return the repos for a config.
///
/// # Safety
/// The config argument must be a non-null Config pointer.
#[no_mangle]
pub unsafe extern "C" fn pkgcraft_config_repos(
    c: *mut Config,
    len: *mut usize,
) -> *mut *const Repo {
    // TODO: switch from usize to std::os::raw::c_size_t when it's stable.
    let config = try_ref_from_ptr!(c);
    iter_to_array!(config.repos.into_iter(), len, |(_, r)| { r as *const _ })
}

/// Return the RepoSet for a given set type.
///
/// # Safety
/// The config argument must be a non-null Config pointer.
#[no_mangle]
pub unsafe extern "C" fn pkgcraft_config_repos_set(c: *mut Config, kind: Repos) -> *mut RepoSet {
    let config = try_ref_from_ptr!(c);
    Box::into_raw(Box::new(config.repos.set(kind)))
}

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