use crate::bindings;
use crate::error::Result;
use std::ffi::{CStr, CString};
use std::ptr;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EncryptionMode {
Never,
IfRequested,
Required,
Always,
}
impl From<bindings::http_encryption_e> for EncryptionMode {
fn from(encryption: bindings::http_encryption_e) -> Self {
match encryption {
bindings::http_encryption_e_HTTP_ENCRYPTION_NEVER => EncryptionMode::Never,
bindings::http_encryption_e_HTTP_ENCRYPTION_IF_REQUESTED => EncryptionMode::IfRequested,
bindings::http_encryption_e_HTTP_ENCRYPTION_REQUIRED => EncryptionMode::Required,
bindings::http_encryption_e_HTTP_ENCRYPTION_ALWAYS => EncryptionMode::Always,
_ => EncryptionMode::IfRequested, }
}
}
impl Into<bindings::http_encryption_e> for EncryptionMode {
fn into(self) -> bindings::http_encryption_e {
match self {
EncryptionMode::Never => bindings::http_encryption_e_HTTP_ENCRYPTION_NEVER,
EncryptionMode::IfRequested => bindings::http_encryption_e_HTTP_ENCRYPTION_IF_REQUESTED,
EncryptionMode::Required => bindings::http_encryption_e_HTTP_ENCRYPTION_REQUIRED,
EncryptionMode::Always => bindings::http_encryption_e_HTTP_ENCRYPTION_ALWAYS,
}
}
}
pub fn get_server() -> String {
unsafe {
let server_ptr = bindings::cupsServer();
if server_ptr.is_null() {
"localhost".to_string() } else {
CStr::from_ptr(server_ptr)
.to_string_lossy()
.into_owned()
}
}
}
pub fn set_server(server: Option<&str>) -> Result<()> {
match server {
Some(s) => {
let server_c = CString::new(s)?;
unsafe {
bindings::cupsSetServer(server_c.as_ptr());
}
}
None => unsafe {
bindings::cupsSetServer(ptr::null());
},
}
Ok(())
}
pub fn get_user() -> String {
unsafe {
let user_ptr = bindings::cupsUser();
if user_ptr.is_null() {
std::env::var("USER")
.or_else(|_| std::env::var("USERNAME"))
.unwrap_or_else(|_| "anonymous".to_string())
} else {
CStr::from_ptr(user_ptr)
.to_string_lossy()
.into_owned()
}
}
}
pub fn set_user(user: Option<&str>) -> Result<()> {
match user {
Some(u) => {
let user_c = CString::new(u)?;
unsafe {
bindings::cupsSetUser(user_c.as_ptr());
}
}
None => unsafe {
bindings::cupsSetUser(ptr::null());
},
}
Ok(())
}
pub fn get_encryption() -> EncryptionMode {
unsafe {
let encryption = bindings::cupsEncryption();
EncryptionMode::from(encryption)
}
}
pub fn set_encryption(mode: EncryptionMode) {
unsafe {
bindings::cupsSetEncryption(mode.into());
}
}
pub fn get_user_agent() -> String {
unsafe {
let agent_ptr = bindings::cupsUserAgent();
if agent_ptr.is_null() {
format!("CUPS/2.4 (cups-rs/{})", env!("CARGO_PKG_VERSION"))
} else {
CStr::from_ptr(agent_ptr)
.to_string_lossy()
.into_owned()
}
}
}
pub fn set_user_agent(user_agent: Option<&str>) -> Result<()> {
match user_agent {
Some(agent) => {
let agent_c = CString::new(agent)?;
unsafe {
bindings::cupsSetUserAgent(agent_c.as_ptr());
}
}
None => unsafe {
bindings::cupsSetUserAgent(ptr::null());
},
}
Ok(())
}
#[derive(Debug)]
pub struct CupsConfig {
original_server: Option<String>,
original_user: Option<String>,
original_encryption: Option<EncryptionMode>,
original_user_agent: Option<String>,
}
impl CupsConfig {
pub fn new() -> Self {
CupsConfig {
original_server: Some(get_server()),
original_user: Some(get_user()),
original_encryption: Some(get_encryption()),
original_user_agent: Some(get_user_agent()),
}
}
pub fn with_server(self, server: &str) -> Result<Self> {
set_server(Some(server))?;
Ok(self)
}
pub fn with_user(self, user: &str) -> Result<Self> {
set_user(Some(user))?;
Ok(self)
}
pub fn with_encryption(self, mode: EncryptionMode) -> Self {
set_encryption(mode);
self
}
pub fn with_user_agent(self, user_agent: &str) -> Result<Self> {
set_user_agent(Some(user_agent))?;
Ok(self)
}
pub fn current_config(&self) -> ConfigSummary {
ConfigSummary {
server: get_server(),
user: get_user(),
encryption: get_encryption(),
user_agent: get_user_agent(),
}
}
}
impl Default for CupsConfig {
fn default() -> Self {
Self::new()
}
}
impl Drop for CupsConfig {
fn drop(&mut self) {
if let Some(server) = &self.original_server {
let _ = set_server(Some(server));
}
if let Some(user) = &self.original_user {
let _ = set_user(Some(user));
}
if let Some(encryption) = &self.original_encryption {
set_encryption(*encryption);
}
if let Some(user_agent) = &self.original_user_agent {
let _ = set_user_agent(Some(user_agent));
}
}
}
#[derive(Debug, Clone)]
pub struct ConfigSummary {
pub server: String,
pub user: String,
pub encryption: EncryptionMode,
pub user_agent: String,
}
impl std::fmt::Display for ConfigSummary {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Server: {}, User: {}, Encryption: {:?}, User-Agent: {}",
self.server, self.user, self.encryption, self.user_agent
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encryption_mode_conversion() {
let modes = [
EncryptionMode::Never,
EncryptionMode::IfRequested,
EncryptionMode::Required,
EncryptionMode::Always,
];
for mode in &modes {
let cups_mode: bindings::http_encryption_e = (*mode).into();
let converted_back = EncryptionMode::from(cups_mode);
assert_eq!(*mode, converted_back);
}
}
#[test]
fn test_server_configuration() {
let original_server = get_server();
let test_server = "test.example.com:8631";
set_server(Some(test_server)).unwrap();
let current_server = get_server();
assert!(current_server.contains("test.example.com"),
"Expected server to contain 'test.example.com', got: {}", current_server);
set_server(None).unwrap();
set_server(Some(&original_server)).unwrap();
}
#[test]
fn test_config_manager() {
let original_server = get_server();
{
let _config = CupsConfig::new()
.with_server("managed.example.com").unwrap()
.with_encryption(EncryptionMode::Required);
assert_eq!(get_server(), "managed.example.com");
assert_eq!(get_encryption(), EncryptionMode::Required);
}
assert_eq!(get_server(), original_server);
}
}