pub mod ffi;
use ffi::*;
use num_enum::TryFromPrimitive;
#[derive(TryFromPrimitive, Debug, Clone)]
#[repr(i32)]
pub enum LinkType {
Binary = 0,
ManPage = 1,
Group = 2,
}
#[derive(TryFromPrimitive, Debug, Clone)]
#[repr(i32)]
pub enum LinkOptions {
None = 0,
KeepARGV0 = 1,
UpdateARGV0 = 2,
}
#[derive(Clone, Debug)]
pub struct Alternative {
pub priority: i32,
pub r#type: LinkType,
pub target: String,
pub options: LinkOptions,
}
pub enum Config {
System,
User,
Custom(String),
}
impl Alternative {
pub fn new<T>(priority: i32, r#type: LinkType, target: T, options: LinkOptions) -> Alternative
where
T: ToString,
{
Alternative {
priority,
r#type,
target: target.to_string(),
options,
}
}
pub fn with_highest_priority<T>(bin: T) -> Result<Alternative, Box<dyn std::error::Error>>
where
T: ToString,
{
let name = std::ffi::CString::new(bin.to_string())?;
unsafe {
let mut alternative_ref: *mut AlternativeLink = std::ptr::null_mut();
if libalts_load_highest_priority_binary_alternatives(
name.as_ptr(),
&mut alternative_ref,
) != 0
{
return Err(Box::new(std::io::Error::last_os_error()));
}
let alternative = alternative_ref.as_ref().unwrap().to_alternative();
libalts_free_alternatives_ptr(&mut alternative_ref);
Ok(alternative)
}
}
pub fn with_priority<T>(bin: T, prio: i32) -> Result<Alternative, Box<dyn std::error::Error>>
where
T: ToString,
{
let name = std::ffi::CString::new(bin.to_string())?;
unsafe {
let mut alternative_ref: *mut AlternativeLink = std::ptr::null_mut();
if libalts_load_exact_priority_binary_alternatives(
name.as_ptr(),
prio,
&mut alternative_ref,
) != 0
{
return Err(Box::new(std::io::Error::last_os_error()));
}
let alternative = alternative_ref.as_ref().unwrap().to_alternative();
libalts_free_alternatives_ptr(&mut alternative_ref);
Ok(alternative)
}
}
pub fn get_binaries() -> Result<Vec<String>, Box<dyn std::error::Error>> {
unsafe {
let mut size: libc::size_t = 0;
let mut binaries_ref = std::ptr::null_mut();
if libalts_load_available_binaries(&mut binaries_ref, &mut size) != 0 {
return Err(Box::new(std::io::Error::last_os_error()));
}
let binaries = Ok(std::slice::from_raw_parts(binaries_ref, size)
.as_ref()
.into_iter()
.map(|s| std::ffi::CStr::from_ptr(*s).to_str().unwrap().to_string())
.collect());
for i in 0..size {
libc::free(*binaries_ref.add(i) as *mut libc::c_void);
}
libc::free(binaries_ref as *mut libc::c_void);
binaries
}
}
pub fn get_binary_alternatives<T>(
bin: T,
) -> Result<Vec<Alternative>, Box<dyn std::error::Error>>
where
T: ToString + Clone,
{
let name = std::ffi::CString::new(bin.to_string())?;
unsafe {
let mut size: libc::size_t = 0;
let mut prios = std::ptr::null_mut();
if libalts_load_binary_priorities(name.as_ptr(), &mut prios, &mut size) != 0 {
return Err(Box::new(std::io::Error::last_os_error()));
}
let mut alts: Vec<Alternative> = Vec::with_capacity(size);
let prios_vec = std::slice::from_raw_parts(prios, size)
.as_ref()
.iter()
.collect::<Vec<&i32>>();
for prio in prios_vec {
alts.push(Self::with_priority(bin.clone(), *prio)?);
}
libc::free(prios as *mut libc::c_void);
Ok(alts)
}
}
pub fn read_priority_from_config<T>(
bin: T,
config_file: Config,
) -> Result<i32, Box<dyn std::error::Error>>
where
T: ToString,
{
let bin = std::ffi::CString::new(bin.to_string())?;
let config_file = std::ffi::CString::new(Self::get_config_path(config_file))?;
let res: i32;
unsafe {
res = libalts_read_binary_configured_priority_from_file(
bin.as_ptr(),
config_file.as_ptr(),
);
}
if res < 0 {
return Err(Box::new(std::io::Error::last_os_error()));
}
Ok(res)
}
pub fn write_priority_to_file<T>(
bin: T,
prio: u32,
config_file: Config,
) -> Result<(), Box<dyn std::error::Error>>
where
T: ToString,
{
let bin = std::ffi::CString::new(bin.to_string())?;
let config_file = std::ffi::CString::new(Self::get_config_path(config_file))?;
let res: i32 = unsafe {
libalts_write_binary_configured_priority_to_file(
bin.as_ptr(),
prio as i32,
config_file.as_ptr(),
)
};
if res != 0 {
return Err(Box::new(std::io::Error::last_os_error()));
}
Ok(())
}
pub fn get_system_config_path() -> String {
unsafe {
let str_ptr = libalts_get_system_config_path();
std::ffi::CStr::from_ptr(str_ptr)
.to_string_lossy()
.to_string()
}
}
pub fn get_user_config_path() -> String {
unsafe {
let str_ptr = libalts_get_system_config_path();
std::ffi::CStr::from_ptr(str_ptr)
.to_string_lossy()
.to_string()
}
}
pub fn get_config_path(config: Config) -> String {
match config {
Config::System => Self::get_system_config_path(),
Config::User => Self::get_user_config_path(),
Config::Custom(x) => x,
}
}
pub fn check_if_config_exists(config: Config) -> Result<bool, Box<dyn std::error::Error>> {
Ok(std::fs::exists(Self::get_config_path(config))?)
}
}
impl std::fmt::Display for Alternative {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Target: {}, Priority: {}, Options: {:#?}, Type: {:#?}",
self.target, self.priority, self.options, self.r#type,
)
}
}