Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion libuci-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ pub use bindings::{
uci_parse_option, uci_parse_ptr, uci_parse_section, uci_perror, uci_ptr,
uci_ptr_UCI_LOOKUP_COMPLETE, uci_rename, uci_reorder_section, uci_revert, uci_save,
uci_section, uci_set, uci_set_backend, uci_set_confdir, uci_set_savedir, uci_type,
uci_type_UCI_TYPE_OPTION, uci_type_UCI_TYPE_SECTION, uci_unload, uci_validate_text, UCI_OK,
uci_type_UCI_TYPE_OPTION, uci_type_UCI_TYPE_SECTION, uci_unload, uci_validate_text,
UCI_ERR_NOTFOUND, UCI_OK,
};

#[allow(non_upper_case_globals)]
Expand Down
1 change: 1 addition & 0 deletions rust-uci/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ targets = ["x86_64-unknown-linux-gnu"]
libuci-sys = { version = "^1.1.0", path = "../libuci-sys" }
log = "^0.4.14"
libc = "^0.2.91"
thiserror = "2"
49 changes: 12 additions & 37 deletions rust-uci/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,22 @@
// SPDX-License-Identifier: MIT OR Apache-2.0

use std::ffi::NulError;
use std::fmt::{Debug, Display, Formatter};
use std::option::Option::None;
use std::fmt::Debug;
use std::str::Utf8Error;

#[derive(Debug, Clone)]
use thiserror::Error;

#[derive(Debug, Clone, Error)]
pub enum Error {
#[error("{0}")]
Message(String),
Utf8Error(Utf8Error),
NulError(NulError),
#[error("{0}")]
Utf8Error(#[from] Utf8Error),
#[error("{0}")]
NulError(#[from] NulError),
/// uci was unable to find the entry for `entry_identifyer`, e.g. during `uci.get()`
#[error("Entry not found: {entry_identifyer}")]
EntryNotFound { entry_identifyer: String },
}

pub type Result<T> = std::result::Result<T, Error>;

impl From<Utf8Error> for Error {
fn from(err: Utf8Error) -> Self {
Self::Utf8Error(err)
}
}

impl From<NulError> for Error {
fn from(err: NulError) -> Self {
Self::NulError(err)
}
}

impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Message(_) => None,
Error::Utf8Error(err) => Some(err),
Error::NulError(err) => Some(err),
}
}
}

impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Error::Message(msg) => Display::fmt(msg, f),
Error::Utf8Error(err) => Display::fmt(err, f),
Error::NulError(err) => Display::fmt(err, f),
}
}
}
50 changes: 31 additions & 19 deletions rust-uci/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ use libuci_sys::{
uci_type_UCI_TYPE_SECTION, uci_unload,
};
use log::debug;
use std::ffi::c_int;
use std::sync::Mutex;
use std::{
ffi::{CStr, CString},
Expand All @@ -65,8 +66,8 @@ use std::{

use crate::error::{Error, Result};

#[allow(clippy::cast_possible_wrap)]
const UCI_OK: i32 = libuci_sys::UCI_OK as i32;
const UCI_OK: c_int = libuci_sys::UCI_OK as c_int;
const UCI_ERR_NOTFOUND: c_int = libuci_sys::UCI_ERR_NOTFOUND as c_int;

// Global lock to ensure that only one instance of libuci function calls is running at the time.
// Necessary because libuci uses thread-unsafe functions with global state (e.g., strtok).
Expand Down Expand Up @@ -140,7 +141,7 @@ impl DerefMut for UciPtr {

impl Drop for UciPtr {
fn drop(&mut self) {
unsafe { CString::from_raw(self.1) };
drop(unsafe { CString::from_raw(self.1) });
}
}

Expand Down Expand Up @@ -216,7 +217,8 @@ impl Uci {
///
/// Allowed keys are like `network.wan.proto`, `network.@interface[-1].iface`, `network.wan` and `network.@interface[-1]`
///
/// if the deletion failed an `Err` is returned.
/// If the deletion failed an `Err` is returned.
/// If the entry does not exist before deletion, an `Err` with [`Error::EntryNotFound`] is returned.
pub fn delete(&mut self, identifier: &str) -> Result<()> {
let mut ptr = self.get_ptr(identifier)?;
libuci_locked!(self, {
Expand All @@ -231,16 +233,18 @@ impl Uci {
)));
}
let result = unsafe { uci_save(self.ctx, ptr.p) };
if result == UCI_OK {
Ok(())
} else {
Err(Error::Message(format!(
match result {
UCI_OK => Ok(()),
UCI_ERR_NOTFOUND => Err(Error::EntryNotFound {
entry_identifyer: identifier.to_string(),
}),
_ => Err(Error::Message(format!(
"Could not save uci key: {}, {}, {}",
identifier,
result,
self.get_last_error()
.unwrap_or_else(|_| String::from("Unknown"))
)))
))),
}
})
}
Expand Down Expand Up @@ -355,7 +359,7 @@ impl Uci {
///
/// Allowed keys are like `network.wan.proto`, `network.@interface[-1].iface`, `network.lan` and `network.@interface[-1]`
///
/// if the entry does not exist an `Err` is returned.
/// If the entry does not exist an `Err` with [`Error::EntryNotFound`] is returned.
pub fn get(&mut self, key: &str) -> Result<String> {
let ptr = libuci_locked!(self, { self.get_ptr(key)? });
if ptr.flags & uci_ptr_UCI_LOOKUP_COMPLETE == 0 {
Expand Down Expand Up @@ -407,7 +411,7 @@ impl Uci {
);
Ok(String::from(typ))
}
_ => return Err(Error::Message(format!("unsupported type: {}", last.type_))),
_ => Err(Error::Message(format!("unsupported type: {}", last.type_))),
}
}

Expand Down Expand Up @@ -436,14 +440,22 @@ impl Uci {
let raw = libuci_locked!(self, {
let raw = CString::new(identifier)?.into_raw();
let result = unsafe { uci_lookup_ptr(self.ctx, &mut ptr, raw, true) };
if result != UCI_OK {
return Err(Error::Message(format!(
"Could not parse uci key: {}, {}, {}",
identifier,
result,
self.get_last_error()
.unwrap_or_else(|_| String::from("Unknown"))
)));
match result {
UCI_OK => (),
UCI_ERR_NOTFOUND => {
return Err(Error::EntryNotFound {
entry_identifyer: identifier.to_string(),
});
}
_ => {
return Err(Error::Message(format!(
"Could not parse uci key: {}, {}, {}",
identifier,
result,
self.get_last_error()
.unwrap_or_else(|_| String::from("Unknown"))
)));
}
}
raw
});
Expand Down