senpy_ffi/lib.rs
1// This file is part of senpy-ffi <https://github.com/senpy-club/senpy-ffi>.
2// Copyright (C) 2022-2022 Fuwn <contact@fuwn.me>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, version 3.
7//
8// This program is distributed in the hope that it will be useful, but
9// WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11// General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with this program. If not, see <http://www.gnu.org/licenses/>.
15//
16// Copyright (C) 2022-2022 Fuwn <contact@fuwn.me>
17// SPDX-License-Identifier: GPL-3.0-only
18
19//! FFI bindings for [`senpy-rs`](https://github.com/senpy-club/senpy-rs)
20
21#![deny(
22 warnings,
23 nonstandard_style,
24 unused,
25 future_incompatible,
26 rust_2018_idioms
27)]
28#![deny(clippy::all, clippy::nursery, clippy::pedantic)]
29#![recursion_limit = "128"]
30#![doc(
31 html_logo_url = "https://senpy.club/favicon.png",
32 html_favicon_url = "https://senpy.club/favicon.png"
33)]
34
35use std::ffi::{CStr, CString};
36
37use libc::c_char;
38
39/// The base URL to The Senpy Club API
40///
41/// FFI binding to `senpy::SENPY_CLUB_API_BASE_URL`
42#[no_mangle]
43pub static SENPY_CLUB_API_BASE_URL: &str = senpy::SENPY_CLUB_API_BASE_URL;
44/// The current API version of The Senpy Club API
45///
46/// FFI binding to `senpy::SENPY_CLUB_API_CURRENT_VERSION`
47#[no_mangle]
48pub static SENPY_CLUB_API_CURRENT_VERSION: u32 =
49 senpy::SENPY_CLUB_API_CURRENT_VERSION;
50/// The API URL to The Senpy Club API
51///
52/// FFI binding to `senpy::SENPY_CLUB_API_URL`
53#[no_mangle]
54pub static SENPY_CLUB_API_URL: &str = senpy::SENPY_CLUB_API_URL;
55
56/// The response of the <https://api.senpy.club/v2/random> route
57///
58/// Part of the FFI binding to `senpy::random`
59#[repr(C)]
60#[derive(Default)]
61pub struct Random {
62 language: String,
63 image: String,
64}
65impl Random {
66 /// Initializes a new `Random`
67 ///
68 /// Part of the FFI binding to `senpy::random`
69 #[must_use]
70 pub fn new() -> Self { Self::default() }
71
72 /// Populates a `Random` from a `senpy::random` call
73 ///
74 /// Part of the FFI binding to `senpy::random`
75 pub fn populate(&mut self) {
76 if let Ok(image) = senpy::random() {
77 self.language = image.language;
78 self.image = image.image;
79 } else {
80 self.language = "".to_string();
81 self.image = "".to_string();
82 }
83 }
84
85 /// Frees a `Random`
86 ///
87 /// Part of the FFI binding to `senpy::random`
88 #[must_use]
89 pub fn get(&self, key: &str) -> String {
90 match key {
91 "language" => self.language.clone(),
92 "image" => self.image.clone(),
93 _ => "".to_string(),
94 }
95 }
96}
97
98/// Returns an array where the first element is the size of the array and the
99/// remaining elements are the images.
100///
101/// If the first element (size) is `-1`; the
102/// request failed for any reason.
103///
104/// # Safety
105/// This is an *unsafe* FFI binding to `senpy::language`.
106///
107/// # Panics
108/// if a `String` cannot be converted into a `CString`
109#[no_mangle]
110pub unsafe extern "C" fn language(language: *const c_char) -> *mut *mut c_char {
111 match senpy::language(CStr::from_ptr(language).to_str().unwrap()) {
112 Ok(images) => {
113 let mut images_c =
114 vec![CString::new(images.len().to_string()).unwrap().into_raw()];
115
116 for image in images {
117 images_c.push(CString::new(image).unwrap().into_raw());
118 }
119
120 images_c.as_mut_ptr()
121 }
122 Err(_) => vec![CString::new("-1").unwrap().into_raw()].as_mut_ptr(),
123 }
124}
125
126/// Returns an array where the first element is the size of the array and the
127/// remaining elements are the languages.
128///
129/// If the first element (size) is `-1`; the request failed for
130/// any reason.
131///
132/// FFI binding to `senpy::languages`
133///
134/// # Panics
135/// if a `String` cannot be converted into a `CString`
136#[no_mangle]
137pub extern "C" fn languages() -> *mut *mut c_char {
138 match senpy::languages() {
139 Ok(languages) => {
140 let mut languages_c = vec![CString::new(languages.len().to_string())
141 .unwrap()
142 .into_raw()];
143
144 for language in languages {
145 languages_c.push(CString::new(language).unwrap().into_raw());
146 }
147
148 languages_c.as_mut_ptr()
149 }
150 Err(_) => vec![CString::new("-1").unwrap().into_raw()].as_mut_ptr(),
151 }
152}
153
154/// Initializes a new `Random`
155///
156/// Part of the FFI binding to `senpy::random`
157#[no_mangle]
158pub extern "C" fn random_new() -> *mut Random {
159 Box::into_raw(Box::new(Random::new()))
160}
161
162/// Populates a `Random` from a `senpy::random` call
163///
164/// # Safety
165/// This is part of an *unsafe* FFI binding to `senpy::random`.
166#[no_mangle]
167pub unsafe extern "C" fn random_populate(random: *mut Random) {
168 (&mut *random).populate();
169}
170
171/// Gets a member from a `Random`, valid members are `language` and `image`.
172///
173/// # Safety
174/// This is part of an *unsafe* FFI binding to `senpy::random`.
175///
176/// # Panics
177/// if the `key` cannot be wrapped as a safe `CStr`
178#[no_mangle]
179pub unsafe extern "C" fn random_get(
180 random: *const Random,
181 key: *const c_char,
182) -> *mut c_char {
183 CString::new((&*random).get(CStr::from_ptr(key).to_str().unwrap()))
184 .unwrap()
185 .into_raw()
186}
187
188/// Frees a `Random`
189///
190/// # Safety
191/// This is part of an *unsafe* FFI binding to `senpy::random`.
192#[no_mangle]
193pub unsafe extern "C" fn random_free(random: *mut Random) {
194 if random.is_null() {
195 return;
196 }
197
198 drop(Box::from_raw(random));
199}
200
201/// Returns `1` if up, returns `0` if down, and returns `-1` if the request
202/// failed for any reason.
203///
204/// FFI binding to `senpy::status`
205#[no_mangle]
206pub extern "C" fn status() -> i32 {
207 match senpy::status() {
208 Ok(status) =>
209 if status {
210 1
211 } else {
212 0
213 },
214 Err(_) => -1,
215 }
216}