diff --git a/src/api/api_model.rs b/src/api/api_model.rs index 0cd4758e1..0db7737da 100644 --- a/src/api/api_model.rs +++ b/src/api/api_model.rs @@ -15,7 +15,7 @@ use crate::repository::storage::{hash_string_as_hex}; /// File-Download information. #[derive(Clone)] -pub(crate) struct FileDownload { +pub struct FileDownload { /// uuid of the download for identification. pub uuid: String, /// `file_dir` is the directory where the download should be placed. @@ -74,14 +74,14 @@ impl FileDownload { // "accept-ranges" => "0-1975828544" // "content-range" => "bytes 0-1975828543/1975828544" - pub fn new(req_url: &str, req_filename: &str, download_cfg: &VideoDownloadConfig) -> Option { + pub fn new(req_url: &str, req_filename: &str, download_cfg: &VideoDownloadConfig) -> Option { match reqwest::Url::parse(req_url) { Ok(url) => { let filename_re = download_cfg.t_re_filename.as_ref().unwrap(); let tmp_filename = filename_re.replace_all(&unidecode(req_filename) .replace(' ', "_"), "") .replace("__", "_") - .replace("_-_", "-").to_string(); + .replace("_-_", "-"); let filename_path = Path::new(&tmp_filename); let file_stem = filename_path.file_stem().and_then(OsStr::to_str).unwrap_or("").trim_matches(FILENAME_TRIM_PATTERNS); let file_ext = filename_path.extension().and_then(OsStr::to_str).unwrap_or(""); @@ -100,7 +100,7 @@ impl FileDownload { file_path.to_str()?; - Some(FileDownload { + Some(Self { uuid: hash_string_as_hex(req_url), file_dir, file_path, @@ -117,20 +117,20 @@ impl FileDownload { } -pub(crate) struct DownloadQueue { +pub struct DownloadQueue { pub queue: Arc>>, pub active: Arc>>, pub finished: Arc>>, } -pub(crate) struct AppState { +pub struct AppState { pub config: Arc, pub targets: Arc, pub downloads: Arc, } #[derive(Serialize)] -pub(crate) struct XtreamUserInfo { +pub struct XtreamUserInfo { pub active_cons: String, pub allowed_output_formats: Vec, //["ts"], @@ -150,7 +150,7 @@ pub(crate) struct XtreamUserInfo { } #[derive(Serialize)] -pub(crate) struct XtreamServerInfo { +pub struct XtreamServerInfo { pub url: String, pub port: String, pub https_port: String, @@ -163,15 +163,15 @@ pub(crate) struct XtreamServerInfo { } #[derive(Serialize)] -pub(crate) struct XtreamAuthorizationResponse { +pub struct XtreamAuthorizationResponse { pub user_info: XtreamUserInfo, pub server_info: XtreamServerInfo, } impl XtreamAuthorizationResponse { - pub(crate) fn new(server_info: &ApiProxyServerInfo, user: &ProxyUserCredentials) -> Self { + pub fn new(server_info: &ApiProxyServerInfo, user: &ProxyUserCredentials) -> Self { let now = Local::now(); - XtreamAuthorizationResponse { + Self { user_info: XtreamUserInfo { active_cons: "0".to_string(), allowed_output_formats: Vec::from(["ts".to_string(), "m3u8".to_string(), "rtmp".to_string()]), @@ -201,7 +201,7 @@ impl XtreamAuthorizationResponse { #[derive(Debug, serde::Serialize, serde::Deserialize, Default)] -pub(crate) struct UserApiRequest { +pub struct UserApiRequest { #[serde(default)] pub username: String, #[serde(default)] @@ -231,7 +231,7 @@ pub(crate) struct UserApiRequest { } #[derive(Deserialize, Serialize, Debug, Clone)] -pub(crate) struct ServerInputConfig { +pub struct ServerInputConfig { pub id: u16, pub input_type: InputType, pub url: String, @@ -243,7 +243,7 @@ pub(crate) struct ServerInputConfig { } #[derive(Deserialize, Serialize, Debug, Clone)] -pub(crate) struct ServerTargetConfig { +pub struct ServerTargetConfig { pub id: u16, pub enabled: bool, pub name: String, @@ -259,13 +259,13 @@ pub(crate) struct ServerTargetConfig { } #[derive(Deserialize, Serialize, Debug, Clone)] -pub(crate) struct ServerSourceConfig { +pub struct ServerSourceConfig { pub inputs: Vec, pub targets: Vec, } #[derive(Deserialize, Serialize, Debug, Clone)] -pub(crate) struct ServerConfig { +pub struct ServerConfig { pub api: ConfigApi, pub threads: u8, pub working_dir: String, @@ -279,25 +279,25 @@ pub(crate) struct ServerConfig { #[derive(Deserialize, Serialize, Debug, Clone)] -pub(crate) struct PlaylistRequest { +pub struct PlaylistRequest { pub url: Option, pub input_id: Option, } -impl From> for PlaylistRequest { - fn from(req: web::Json) -> Self { +impl From> for PlaylistRequest { + fn from(req: web::Json) -> Self { req.clone() } } #[derive(Deserialize, Serialize, Debug, Clone)] -pub(crate) struct FileDownloadRequest { +pub struct FileDownloadRequest { pub url: String, pub filename: String, } -impl From> for FileDownloadRequest { - fn from(req: web::Json) -> Self { +impl From> for FileDownloadRequest { + fn from(req: web::Json) -> Self { req.clone() } } diff --git a/src/api/api_utils.rs b/src/api/api_utils.rs index feab8798b..e83e24938 100644 --- a/src/api/api_utils.rs +++ b/src/api/api_utils.rs @@ -9,20 +9,20 @@ use crate::model::api_proxy::{ApiProxyServerInfo, ProxyUserCredentials}; use crate::model::config::{Config, ConfigTarget, ConfigInput}; use crate::utils::request_utils; -pub(crate) async fn serve_file(file_path: &Path, req: &HttpRequest, mime_type: mime::Mime) -> HttpResponse { +pub async fn serve_file(file_path: &Path, req: &HttpRequest, mime_type: mime::Mime) -> HttpResponse { if file_path.exists() { if let Ok(file) = actix_files::NamedFile::open_async(file_path).await { let mut result = file.set_content_type(mime_type) .disable_content_disposition().into_response(req); let headers = result.headers_mut(); - headers.insert(CACHE_CONTROL, HeaderValue::from_bytes("no-cache".as_bytes()).unwrap()); + headers.insert(CACHE_CONTROL, HeaderValue::from_bytes(b"no-cache").unwrap()); return result; } } HttpResponse::NoContent().finish() } -pub(crate) fn get_user_target_by_credentials<'a>(username: &str, password: &str, api_req: &'a UserApiRequest, +pub fn get_user_target_by_credentials<'a>(username: &str, password: &str, api_req: &'a UserApiRequest, app_state: &'a web::Data) -> Option<(ProxyUserCredentials, &'a ConfigTarget)> { if !username.is_empty() && !password.is_empty() { app_state.config.get_target_for_user(username, password) @@ -36,25 +36,19 @@ pub(crate) fn get_user_target_by_credentials<'a>(username: &str, password: &str, } } -pub(crate) fn get_user_target<'a>(api_req: &'a UserApiRequest, app_state: &'a web::Data) -> Option<(ProxyUserCredentials, &'a ConfigTarget)> { +pub fn get_user_target<'a>(api_req: &'a UserApiRequest, app_state: &'a web::Data) -> Option<(ProxyUserCredentials, &'a ConfigTarget)> { let username = api_req.username.as_str().trim(); let password = api_req.password.as_str().trim(); get_user_target_by_credentials(username, password, api_req, app_state) } -pub(crate) fn get_user_server_info(cfg: &Config, user: &ProxyUserCredentials) -> ApiProxyServerInfo { +pub fn get_user_server_info(cfg: &Config, user: &ProxyUserCredentials) -> ApiProxyServerInfo { let server_info_list = cfg.t_api_proxy.read().unwrap().as_ref().unwrap().server.clone(); - let server_info_name = match &user.server { - Some(server_name) => server_name.as_str(), - None => "default" - }; - match server_info_list.iter().find(|c| c.name.eq(server_info_name)) { - Some(info) => info.clone(), - None => server_info_list.first().unwrap().clone(), - } + let server_info_name = user.server.as_ref().map_or("default", |server_name| server_name.as_str()); + server_info_list.iter().find(|c| c.name.eq(server_info_name)).map_or_else(|| server_info_list.first().unwrap().clone(), std::clone::Clone::clone) } -pub(crate) async fn stream_response(stream_url: &str, req: &HttpRequest, input: Option<&ConfigInput>) -> HttpResponse { +pub async fn stream_response(stream_url: &str, req: &HttpRequest, input: Option<&ConfigInput>) -> HttpResponse { let req_headers: HashMap<&str, &[u8]> = req.headers().iter().map(|(k, v)| (k.as_str(), v.as_bytes())).collect(); debug!("Try to open stream {}", stream_url); if let Ok(url) = Url::parse(stream_url) { diff --git a/src/api/download_api.rs b/src/api/download_api.rs index c89103760..4dff741c7 100644 --- a/src/api/download_api.rs +++ b/src/api/download_api.rs @@ -106,7 +106,7 @@ macro_rules! download_info { } } -pub(crate) async fn queue_download_file( +pub async fn queue_download_file( req: web::Json, app_state: web::Data, ) -> HttpResponse { @@ -133,19 +133,15 @@ pub(crate) async fn queue_download_file( } } -pub(crate) async fn download_file_info( +pub async fn download_file_info( app_state: web::Data, ) -> HttpResponse { let finished_list: &[Value] = &app_state.downloads.finished.write().unwrap().drain(..) .map(|fd| download_info!(fd)).collect::>(); - match &*app_state.downloads.active.read().unwrap() { - None => HttpResponse::Ok().json(json!({ + (*app_state.downloads.active.read().unwrap()).as_ref().map_or_else(|| HttpResponse::Ok().json(json!({ "completed": true, "downloads": finished_list - })), - Some(file_download) => - HttpResponse::Ok().json(json!({ + })), |file_download| HttpResponse::Ok().json(json!({ "completed": false, "downloads": finished_list, "active": download_info!(file_download) - })) - } + }))) } \ No newline at end of file diff --git a/src/api/m3u_api.rs b/src/api/m3u_api.rs index 2854033f9..b1247bff1 100644 --- a/src/api/m3u_api.rs +++ b/src/api/m3u_api.rs @@ -67,7 +67,7 @@ async fn m3u_api_stream( HttpResponse::BadRequest().finish() } -pub(crate) fn m3u_api_register(cfg: &mut web::ServiceConfig) { +pub fn m3u_api_register(cfg: &mut web::ServiceConfig) { cfg.service(web::resource("/get.php").route(web::get().to(m3u_api))) .service(web::resource("/get.php").route(web::post().to(m3u_api))) .service(web::resource("/apiget").route(web::get().to(m3u_api))) diff --git a/src/api/main_api.rs b/src/api/main_api.rs index a7f498b43..7bfc0cb37 100644 --- a/src/api/main_api.rs +++ b/src/api/main_api.rs @@ -40,7 +40,7 @@ async fn healthcheck() -> HttpResponse { } #[actix_web::main] -pub(crate) async fn start_server(cfg: Arc, targets: Arc) -> futures::io::Result<()> { +pub async fn start_server(cfg: Arc, targets: Arc) -> futures::io::Result<()> { let host = cfg.api.host.to_string(); let port = cfg.api.port; let web_ui_enabled = cfg.web_ui_enabled; diff --git a/src/api/mod.rs b/src/api/mod.rs index 9f97f7f37..3ae92b711 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -1,6 +1,6 @@ -pub(crate) mod api_utils; -pub(crate) mod api_model; -pub(crate) mod main_api; +pub mod api_utils; +pub mod api_model; +pub mod main_api; mod download_api; mod v1_api; mod xtream_api; diff --git a/src/api/scheduler.rs b/src/api/scheduler.rs index a7286c739..f8ac0d126 100644 --- a/src/api/scheduler.rs +++ b/src/api/scheduler.rs @@ -24,7 +24,7 @@ fn datetime_to_instant(datetime: DateTime) -> Instant { Instant::now() + duration_until } -pub(crate) async fn start_scheduler(expression: &str, data: Data) -> ! { +pub async fn start_scheduler(expression: &str, data: Data) -> ! { match Schedule::from_str(expression) { Ok(schedule) => { let offset = *Local::now().offset(); diff --git a/src/api/v1_api.rs b/src/api/v1_api.rs index 07d14364c..e3af0dc0a 100644 --- a/src/api/v1_api.rs +++ b/src/api/v1_api.rs @@ -15,7 +15,7 @@ use crate::model::config::{Config, ConfigDto, ConfigInput, ConfigInputOptions, C use crate::processing::playlist_processor; use crate::utils::{config_reader, download}; -fn _save_config_api_proxy(backup_dir: &str, api_proxy: &mut ApiProxyConfig, file_path: &str) -> Option { +fn _save_config_api_proxy(backup_dir: &str, api_proxy: &ApiProxyConfig, file_path: &str) -> Option { match config_reader::save_api_proxy(file_path, backup_dir, api_proxy) { Ok(()) => {} Err(err) => { @@ -220,7 +220,7 @@ async fn config( HttpResponse::Ok().json(result) } -pub(crate) fn v1_api_register(web_auth_enabled: bool) -> impl Fn(&mut web::ServiceConfig) { +pub fn v1_api_register(web_auth_enabled: bool) -> impl Fn(&mut web::ServiceConfig) { return move |cfg: &mut web::ServiceConfig| { cfg.service(web::scope("/api/v1") .wrap(Condition::new(web_auth_enabled, HttpAuthentication::with_fn(validator))) diff --git a/src/api/web_index.rs b/src/api/web_index.rs index 5d3313309..28097ac85 100644 --- a/src/api/web_index.rs +++ b/src/api/web_index.rs @@ -69,7 +69,7 @@ async fn index( NamedFile::open(path) } -pub(crate) fn index_register(web_dir_path: &Path) -> impl Fn(&mut web::ServiceConfig) + '_ { +pub fn index_register(web_dir_path: &Path) -> impl Fn(&mut web::ServiceConfig) + '_ { return move |cfg: &mut web::ServiceConfig| { cfg.service(web::scope("/auth") .route("/token", web::post().to(token)) diff --git a/src/api/xmltv_api.rs b/src/api/xmltv_api.rs index 777321a00..686906604 100644 --- a/src/api/xmltv_api.rs +++ b/src/api/xmltv_api.rs @@ -28,16 +28,13 @@ fn time_correct(date_time: &str, correction: &TimeDelta) -> String { } // Parse the datetime string - match NaiveDateTime::parse_from_str(date_time_split[0], "%Y%m%d%H%M%S") { - Ok(native_dt) => { + NaiveDateTime::parse_from_str(date_time_split[0], "%Y%m%d%H%M%S").map_or_else(|_| date_time.to_string(), |native_dt| { let corrected_dt = native_dt + *correction; // Format the corrected datetime back to string let formatted_dt = corrected_dt.format("%Y%m%d%H%M%S").to_string(); let result = format!("{} {}", formatted_dt, date_time_split[1]); result - } - Err(_) => date_time.to_string() - } + }) } fn get_epg_path_for_target_of_type(target_name: &str, epg_path: PathBuf) -> Option { @@ -69,9 +66,7 @@ fn get_epg_path_for_target(config: &Config, target: &ConfigTarget) -> Option) -> Option { - match time_shift { - None => None, - Some(offset) => { + time_shift.and_then(|offset| { let sign_factor = if offset.starts_with('-') { -1 } else { 1 }; let offset = offset.trim_start_matches(&['-', '+'][..]); // Remove the sign for parsing @@ -104,8 +99,7 @@ fn parse_timeshift(time_shift: Option<&String>) -> Option { } else { None } - } - } + }) } async fn serve_epg(epg_path: &Path, req: &HttpRequest, user: &ProxyUserCredentials) -> HttpResponse { @@ -203,7 +197,7 @@ async fn xmltv_api( r#""#) } -pub(crate) fn xmltv_api_register(cfg: &mut web::ServiceConfig) { +pub fn xmltv_api_register(cfg: &mut web::ServiceConfig) { cfg.service(web::resource("/xmltv.php").route(web::get().to(xmltv_api))) .service(web::resource("/epg").route(web::get().to(xmltv_api))); } diff --git a/src/api/xtream_api.rs b/src/api/xtream_api.rs index ed09eb992..1acc1fc2e 100644 --- a/src/api/xtream_api.rs +++ b/src/api/xtream_api.rs @@ -100,11 +100,11 @@ impl XtreamApiStreamContext { impl Display for XtreamApiStreamContext { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", match self { - XtreamApiStreamContext::LiveAlt => Self::LIVE_ALT, - XtreamApiStreamContext::Live => Self::LIVE, - XtreamApiStreamContext::Movie => Self::MOVIE, - XtreamApiStreamContext::Series => Self::SERIES, - XtreamApiStreamContext::Timeshift => Self::TIMESHIFT, + Self::LiveAlt => Self::LIVE_ALT, + Self::Live => Self::LIVE, + Self::Movie => Self::MOVIE, + Self::Series => Self::SERIES, + Self::Timeshift => Self::TIMESHIFT, }) } } @@ -118,7 +118,7 @@ struct XtreamApiStreamRequest<'a> { } impl<'a> XtreamApiStreamRequest<'a> { - pub fn from(context: XtreamApiStreamContext, + pub const fn from(context: XtreamApiStreamContext, username: &'a str, password: &'a str, stream_id: &'a str, @@ -133,7 +133,7 @@ impl<'a> XtreamApiStreamRequest<'a> { } } -pub(crate) fn serve_query(file_path: &Path, filter: &HashMap<&str, &str>) -> HttpResponse { +pub fn serve_query(file_path: &Path, filter: &HashMap<&str, &str>) -> HttpResponse { let filtered = json_utils::json_filter_file(file_path, filter); HttpResponse::Ok().json(filtered) } @@ -181,13 +181,11 @@ fn get_user_info(user: &ProxyUserCredentials, cfg: &Config) -> XtreamAuthorizati } fn xtream_api_request_separate_number_and_rest(input: &str) -> (String, String) { - if let Some(dot_index) = input.find('.') { + input.find('.').map_or_else(|| (input.to_string(), String::new()), |dot_index| { let number_part = input[..dot_index].to_string(); let rest = input[dot_index..].to_string(); (number_part, rest) - } else { - (input.to_string(), String::new()) - } + }) } async fn xtream_player_api_stream( @@ -443,10 +441,7 @@ async fn xtream_get_catchup_response(app_state: &AppState, target: &ConfigTarget return HttpResponse::BadRequest().finish(); } - match serde_json::to_string(&doc) { - Ok(result) => HttpResponse::Ok().content_type(mime::APPLICATION_JSON).body(result), - Err(_) => HttpResponse::BadRequest().finish(), - } + serde_json::to_string(&doc).map_or_else(|_| HttpResponse::BadRequest().finish(), |result| HttpResponse::Ok().content_type(mime::APPLICATION_JSON).body(result)) } async fn xtream_player_api( @@ -559,7 +554,7 @@ async fn xtream_player_api_post(req: HttpRequest, xtream_player_api(&req, api_req.into_inner(), &app_state).await } -pub(crate) fn xtream_api_register(cfg: &mut web::ServiceConfig) { +pub fn xtream_api_register(cfg: &mut web::ServiceConfig) { cfg.service(web::resource("/player_api.php").route(web::get().to(xtream_player_api_get)).route(web::post().to(xtream_player_api_get))) .service(web::resource("/panel_api.php").route(web::get().to(xtream_player_api_get)).route(web::post().to(xtream_player_api_get))) .service(web::resource("/xtream").route(web::get().to(xtream_player_api_get)).route(web::post().to(xtream_player_api_post))) diff --git a/src/auth/authenticator.rs b/src/auth/authenticator.rs index ceb494269..5ebca65a1 100644 --- a/src/auth/authenticator.rs +++ b/src/auth/authenticator.rs @@ -12,7 +12,7 @@ pub struct Claims { exp: i64, } -pub(crate) fn create_jwt(web_auth_config: &WebAuthConfig) -> Result { +pub fn create_jwt(web_auth_config: &WebAuthConfig) -> Result { let mut header = Header::new(Algorithm::HS256); header.typ = Some("JWT".to_string()); let now = Local::now(); @@ -29,7 +29,7 @@ pub(crate) fn create_jwt(web_auth_config: &WebAuthConfig) -> Result, secret_key: &[u8]) -> bool { +pub fn verify_token(bearer: Option, secret_key: &[u8]) -> bool { if let Some(auth) = bearer { let token = auth.token(); let token_message = decode::(token, &DecodingKey::from_secret(secret_key), &Validation::new(Algorithm::HS256)); @@ -40,7 +40,7 @@ pub(crate) fn verify_token(bearer: Option, secret_key: &[u8]) -> boo false } -pub(crate) async fn validator( +pub async fn validator( req: ServiceRequest, credentials: Option, ) -> Result { @@ -53,7 +53,7 @@ pub(crate) async fn validator( } } -// pub(crate) fn handle_unauthorized(srvres: ServiceResponse) -> actix_web::Result> { +// pub fn handle_unauthorized(srvres: ServiceResponse) -> actix_web::Result> { // let (req, _) = srvres.into_parts(); // let resp = HttpResponse::TemporaryRedirect().insert_header(("Location", "/auth/login")).finish(); // let result = ServiceResponse::new(req, resp) diff --git a/src/auth/mod.rs b/src/auth/mod.rs index c07a2228f..ce5eb3a4c 100644 --- a/src/auth/mod.rs +++ b/src/auth/mod.rs @@ -1,3 +1,3 @@ -pub(crate) mod authenticator; -pub(crate) mod password; -pub(crate) mod user; \ No newline at end of file +pub mod authenticator; +pub mod password; +pub mod user; \ No newline at end of file diff --git a/src/auth/password.rs b/src/auth/password.rs index e889320b7..91c7db1c1 100644 --- a/src/auth/password.rs +++ b/src/auth/password.rs @@ -11,7 +11,7 @@ fn generate_salt(length: usize) -> String { salt } -pub(crate) fn hash(password: &[u8]) -> Option { +pub fn hash(password: &[u8]) -> Option { let salt = generate_salt(64); if !password.is_empty() { let config = argon2::Config::default(); @@ -22,15 +22,14 @@ pub(crate) fn hash(password: &[u8]) -> Option { None } -pub(crate) fn verify_password(hash: &str, password: &[u8]) -> bool { +pub fn verify_password(hash: &str, password: &[u8]) -> bool { if let Ok(valid) = argon2::verify_encoded(hash, password) { return valid; } false } - -pub(crate) fn generate_password() -> std::io::Result { +pub fn generate_password() -> std::io::Result { match rpassword::prompt_password("password> ") { Ok(pwd1) => { if pwd1.len() < 8 { @@ -39,10 +38,7 @@ pub(crate) fn generate_password() -> std::io::Result { match rpassword::prompt_password("retype password> ") { Ok(pwd2) => { if pwd1.eq(&pwd2) { - match hash(pwd1.as_bytes()) { - None => Err(std::io::Error::new(ErrorKind::Other, "Failed to generate hash")), - Some(hash) => Ok(hash), - } + hash(pwd1.as_bytes()).map_or_else(|| Err(std::io::Error::new(ErrorKind::Other, "Failed to generate hash")), Ok) } else { Err(std::io::Error::new(ErrorKind::Other, "Passwords don't match")) } diff --git a/src/auth/user.rs b/src/auth/user.rs index 59adfce2f..0ac46c74a 100644 --- a/src/auth/user.rs +++ b/src/auth/user.rs @@ -2,14 +2,14 @@ use std::ptr; #[allow(clippy::unsafe_derive_deserialize)] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub(crate) struct UserCredential { +pub struct UserCredential { pub username: String, pub password: String, } impl UserCredential { - pub(crate) fn zeroize(&mut self) { + pub fn zeroize(&mut self) { let password_ptr = self.password.as_mut_ptr(); let password_len = self.password.len(); unsafe { diff --git a/src/filter.rs b/src/filter.rs index ab1bc98a4..ade3e0436 100644 --- a/src/filter.rs +++ b/src/filter.rs @@ -15,7 +15,7 @@ use crate::model::playlist::{PlaylistItem, PlaylistItemType}; use crate::utils::directed_graph::DirectedGraph; use crate::{create_m3u_filter_error_result, exit}; -pub(crate) fn get_field_value(pli: &PlaylistItem, field: &ItemField) -> Rc { +pub fn get_field_value(pli: &PlaylistItem, field: &ItemField) -> Rc { let header = pli.header.borrow(); let value = match field { ItemField::Group => &header.group, @@ -27,7 +27,7 @@ pub(crate) fn get_field_value(pli: &PlaylistItem, field: &ItemField) -> Rc) { +pub fn set_field_value(pli: &PlaylistItem, field: &ItemField, value: Rc) { let header = &mut pli.header.borrow_mut(); match field { ItemField::Group => header.group = value, @@ -38,8 +38,8 @@ pub(crate) fn set_field_value(pli: &mut PlaylistItem, field: &ItemField, value: }; } -pub(crate) struct ValueProvider<'a> { - pub(crate) pli: RefCell<&'a PlaylistItem>, +pub struct ValueProvider<'a> { + pub pli: RefCell<&'a PlaylistItem>, } impl<'a> ValueProvider<'a> { @@ -49,11 +49,11 @@ impl<'a> ValueProvider<'a> { } } -pub(crate) trait ValueProcessor { +pub trait ValueProcessor { fn process(&mut self, field: &ItemField, value: &str, rewc: &RegexWithCaptures) -> bool; } -pub(crate) struct MockValueProcessor {} +pub struct MockValueProcessor {} impl ValueProcessor for MockValueProcessor { fn process(&mut self, _: &ItemField, _: &str, _: &RegexWithCaptures) -> bool { @@ -62,13 +62,13 @@ impl ValueProcessor for MockValueProcessor { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub(crate) struct PatternTemplate { +pub struct PatternTemplate { pub name: String, pub value: String, } #[derive(Debug, Clone)] -pub(crate) struct RegexWithCaptures { +pub struct RegexWithCaptures { pub restr: String, pub re: regex::Regex, pub captures: Vec, @@ -101,12 +101,12 @@ main = _{ SOI ~ stmt ~ EOI } struct FilterParser; #[derive(Debug, Clone)] -pub(crate) enum UnaryOperator { +pub enum UnaryOperator { Not } #[derive(Debug, Clone)] -pub(crate) enum BinaryOperator { +pub enum BinaryOperator { And, Or, } @@ -119,14 +119,14 @@ impl BinaryOperator { impl std::fmt::Display for BinaryOperator { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", match *self { - BinaryOperator::Or => Self::OP_OR, - BinaryOperator::And => Self::OP_AND, + Self::Or => Self::OP_OR, + Self::And => Self::OP_AND, }) } } #[derive(Debug, Clone)] -pub(crate) enum Filter { +pub enum Filter { Group(Box), FieldComparison(ItemField, RegexWithCaptures), TypeComparison(ItemField, PlaylistItemType), @@ -137,7 +137,7 @@ pub(crate) enum Filter { impl Filter { pub fn filter(&self, provider: &ValueProvider, processor: &mut dyn ValueProcessor) -> bool { match self { - Filter::FieldComparison(field, rewc) => { + Self::FieldComparison(field, rewc) => { let value = provider.call(field); let is_match = rewc.re.is_match(value.as_str()); if log_enabled!(Level::Trace) { @@ -152,11 +152,9 @@ impl Filter { } is_match } - Filter::TypeComparison(field, item_type) => { + Self::TypeComparison(field, item_type) => { let value = provider.call(field); - match get_filter_item_type(value.as_str()) { - None => false, - Some(pli_type) => { + get_filter_item_type(value.as_str()).map_or(false, |pli_type| { let is_match = pli_type.eq(item_type); if log_enabled!(Level::Trace) { if is_match { @@ -166,18 +164,17 @@ impl Filter { } } is_match - } - } + }) } - Filter::Group(expr) => { + Self::Group(expr) => { expr.filter(provider, processor) } - Filter::UnaryExpression(op, expr) => { + Self::UnaryExpression(op, expr) => { match op { UnaryOperator::Not => !expr.filter(provider, processor), } } - Filter::BinaryExpression(left, op, right) => { + Self::BinaryExpression(left, op, right) => { match op { BinaryOperator::And => left.filter(provider, processor) && right.filter(provider, processor), @@ -199,10 +196,10 @@ impl Filter { impl std::fmt::Display for Filter { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { - Filter::FieldComparison(field, rewc) => { + Self::FieldComparison(field, rewc) => { write!(f, "{} ~ \"{}\"", field, String::from(&rewc.restr)) } - Filter::TypeComparison(field, item_type) => { + Self::TypeComparison(field, item_type) => { write!(f, "{} = {}", field, match item_type { PlaylistItemType::Live => Self::LIVE, PlaylistItemType::Video => Self::VOD, @@ -210,16 +207,16 @@ impl std::fmt::Display for Filter { _ => Self::UNSUPPORTED }) } - Filter::Group(stmt) => { + Self::Group(stmt) => { write!(f, "({stmt})") } - Filter::UnaryExpression(op, expr) => { + Self::UnaryExpression(op, expr) => { let flt = match op { UnaryOperator::Not => format!("NOT {expr}"), }; write!(f, "{flt}") } - Filter::BinaryExpression(left, op, right) => { + Self::BinaryExpression(left, op, right) => { write!(f, "{left} {op} {right}") } } @@ -296,10 +293,7 @@ fn get_parser_type_comparison(expr: Pair) -> Result create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "cant parse item type: {text_item_type}"), - Some(itype) => Ok(Filter::TypeComparison(ItemField::Type, itype)) - } + item_type.map_or_else(|| create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "cant parse item type: {text_item_type}"), |itype| Ok(Filter::TypeComparison(ItemField::Type, itype))) } macro_rules! handle_expr { @@ -388,7 +382,7 @@ fn get_parser_binary_op(expr: &Pair) -> Result>) -> Result { +pub fn get_filter(filter_text: &str, templates: Option<&Vec>) -> Result { let empty_list = Vec::new(); let template_list: &Vec = templates.unwrap_or(&empty_list); let source = apply_templates_to_pattern(filter_text, template_list); @@ -441,12 +435,7 @@ pub(crate) fn get_filter(filter_text: &str, templates: Option<&Vec Ok(filter), - _ => { - create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "Unable to parse filter: {}", &filter_text) - } - } + result.map_or_else(|| create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "Unable to parse filter: {}", &filter_text), Ok) } Err(err) => create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "{}", err) } @@ -476,7 +465,7 @@ fn build_dependency_graph(templates: &Vec) -> Result) -> Result, M3uFilterError> { +pub fn prepare_templates(templates: &Vec) -> Result, M3uFilterError> { let graph = build_dependency_graph(templates)?; let mut template_values = HashMap::::new(); let mut template_map: HashMap = templates.iter() @@ -508,7 +497,7 @@ pub(crate) fn prepare_templates(templates: &Vec) -> Result) -> String { +pub fn apply_templates_to_pattern(pattern: &str, templates: &Vec) -> String { let mut new_pattern = pattern.to_string(); for template in templates { new_pattern = new_pattern.replace(format!("!{}!", &template.name).as_str(), &template.value); diff --git a/src/m3u_filter_error.rs b/src/m3u_filter_error.rs index 382110efd..cb05b2e8e 100644 --- a/src/m3u_filter_error.rs +++ b/src/m3u_filter_error.rs @@ -21,23 +21,23 @@ macro_rules! get_errors_notify_message { } } -#[derive(Debug, PartialEq)] -pub(crate) enum M3uFilterErrorKind { +#[derive(Debug, PartialEq, Eq)] +pub enum M3uFilterErrorKind { // do not send with messaging Info, Notify, // send with messaging } #[derive(Debug)] -pub(crate) struct M3uFilterError { +pub struct M3uFilterError { pub kind: M3uFilterErrorKind, pub message: String, } impl M3uFilterError { - pub fn new(kind: M3uFilterErrorKind, message: String) -> Self { - M3uFilterError { + pub const fn new(kind: M3uFilterErrorKind, message: String) -> Self { + Self { kind, message, } diff --git a/src/main.rs b/src/main.rs index a83ca8309..c81ec6082 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +#![allow(clippy::module_name_repetitions)] extern crate env_logger; extern crate pest; #[macro_use] @@ -17,7 +18,6 @@ use crate::model::config::{Config, HealthcheckConfig, ProcessTargets, validate_t use crate::model::healthcheck::Healthcheck; use crate::processing::playlist_processor; use crate::utils::{config_reader, file_utils}; - mod m3u_filter_error; mod model; mod filter; @@ -81,14 +81,14 @@ fn main() { let default_log_level = std::env::var("M3U_FILTER_LOG").unwrap_or_else(|_| "info".to_string()); init_logger(args.log_level.as_ref().unwrap_or(&default_log_level)); - let config_path: String = args.config_path.unwrap_or(file_utils::get_default_config_path()); - let config_file: String = args.config_file.unwrap_or(file_utils::get_default_config_file_path(&config_path)); + let config_path: String = args.config_path.unwrap_or_else(file_utils::get_default_config_path); + let config_file: String = args.config_file.unwrap_or_else(|| file_utils::get_default_config_file_path(&config_path)); if args.healthcheck { healthcheck(config_file.as_str()); } - let sources_file: String = args.source_file.unwrap_or(file_utils::get_default_sources_file_path(&config_path)); + let sources_file: String = args.source_file.unwrap_or_else(|| file_utils::get_default_sources_file_path(&config_path)); let mut cfg = config_reader::read_config(config_path.as_str(), config_file.as_str(), sources_file.as_str()).unwrap_or_else(|err| exit!("{}", err)); if args.genpwd { diff --git a/src/messaging.rs b/src/messaging.rs index fd53f00db..f41dc2059 100644 --- a/src/messaging.rs +++ b/src/messaging.rs @@ -2,8 +2,8 @@ use log::{debug, error}; use reqwest::header; use crate::model::config::{MessagingConfig}; -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)] -pub(crate) enum MsgKind { +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)] +pub enum MsgKind { #[serde(rename = "info")] Info, #[serde(rename = "stats")] @@ -18,7 +18,7 @@ fn is_enabled(kind: &MsgKind, cfg: &MessagingConfig) -> bool { cfg.notify_on.contains(kind) } -pub(crate) fn send_message(kind: &MsgKind, cfg: &Option, msg: &str) { +pub fn send_message(kind: &MsgKind, cfg: &Option, msg: &str) { if let Some(messaging) = cfg { if is_enabled(kind, messaging) { if let Some(telegram) = &messaging.telegram { diff --git a/src/model/api_proxy.rs b/src/model/api_proxy.rs index 412951bb5..df46a2be2 100644 --- a/src/model/api_proxy.rs +++ b/src/model/api_proxy.rs @@ -8,8 +8,8 @@ use crate::create_m3u_filter_error_result; use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind}; use crate::utils::config_reader; -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Sequence, PartialEq)] -pub(crate) enum ProxyType { +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Sequence, PartialEq, Eq)] +pub enum ProxyType { #[serde(rename = "reverse")] Reverse, #[serde(rename = "redirect")] @@ -51,7 +51,7 @@ impl FromStr for ProxyType { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub(crate) struct ProxyUserCredentials { +pub struct ProxyUserCredentials { pub username: String, pub password: String, pub token: Option, @@ -98,7 +98,7 @@ impl ProxyUserCredentials { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub(crate) struct TargetUser { +pub struct TargetUser { pub target: String, pub credentials: Vec, } @@ -119,7 +119,7 @@ fn default_as_443() -> String { "443".to_string() } fn default_as_1935() -> String { "1935".to_string() } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub(crate) struct ApiProxyServerInfo { +pub struct ApiProxyServerInfo { pub name: String, pub protocol: String, pub host: String, @@ -171,7 +171,7 @@ impl ApiProxyServerInfo { true } - pub(crate) fn get_base_url(&self) -> String { + pub fn get_base_url(&self) -> String { if self.protocol.eq("http") { format!("http://{}:{}", self.host, self.http_port) } else { @@ -181,7 +181,7 @@ impl ApiProxyServerInfo { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub(crate) struct ApiProxyConfig { +pub struct ApiProxyConfig { pub server: Vec, pub user: Vec, } diff --git a/src/model/config.rs b/src/model/config.rs index da1698fd1..94ee74a09 100644 --- a/src/model/config.rs +++ b/src/model/config.rs @@ -24,15 +24,15 @@ use crate::utils::{config_reader, file_utils}; use crate::utils::default_utils::{default_as_default, default_as_true, default_as_two_u16}; use crate::utils::file_lock_manager::FileLockManager; -pub(crate) const MAPPER_ATTRIBUTE_FIELDS: &[&str] = &[ +pub const MAPPER_ATTRIBUTE_FIELDS: &[&str] = &[ "name", "title", "group", "id", "chno", "logo", "logo_small", "parent_code", "audio_track", "time_shift", "rec", "url", "epg_channel_id", "epg_id" ]; -pub(crate) const AFFIX_FIELDS: &[&str] = &["name", "title", "group"]; -pub(crate) const COUNTER_FIELDS: &[&str] = &["name", "title", "chno"]; +pub const AFFIX_FIELDS: &[&str] = &["name", "title", "group"]; +pub const COUNTER_FIELDS: &[&str] = &["name", "title", "chno"]; #[macro_export] macro_rules! valid_property { @@ -84,7 +84,7 @@ macro_rules! handle_m3u_filter_error_result { #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Sequence, PartialEq, Eq, Hash)] -pub(crate) enum TargetType { +pub enum TargetType { #[serde(rename = "m3u")] M3u, #[serde(rename = "xtream")] @@ -109,8 +109,8 @@ impl Display for TargetType { } } -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Sequence, PartialEq, Default)] -pub(crate) enum ProcessingOrder { +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Sequence, PartialEq, Eq, Default)] +pub enum ProcessingOrder { #[serde(rename = "frm")] #[default] Frm, @@ -149,7 +149,7 @@ impl Display for ProcessingOrder { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Sequence)] -pub(crate) enum ItemField { +pub enum ItemField { #[serde(rename = "group")] Group, #[serde(rename = "name")] @@ -183,7 +183,7 @@ impl Display for ItemField { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub(crate) enum FilterMode { +pub enum FilterMode { #[serde(rename = "discard")] Discard, #[serde(rename = "include")] @@ -191,7 +191,7 @@ pub(crate) enum FilterMode { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub(crate) enum SortOrder { +pub enum SortOrder { #[serde(rename = "asc")] Asc, #[serde(rename = "desc")] @@ -200,7 +200,7 @@ pub(crate) enum SortOrder { #[derive(Clone)] -pub(crate) struct ProcessTargets { +pub struct ProcessTargets { pub enabled: bool, pub inputs: Vec, pub targets: Vec, @@ -217,12 +217,12 @@ impl ProcessTargets { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub(crate) struct ConfigSortGroup { +pub struct ConfigSortGroup { pub order: SortOrder, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub(crate) struct ConfigSortChannel { +pub struct ConfigSortChannel { pub field: ItemField, // channel field pub group_pattern: String, @@ -234,7 +234,7 @@ pub(crate) struct ConfigSortChannel { } impl ConfigSortChannel { - pub(crate) fn prepare(&mut self) -> Result<(), M3uFilterError> { + pub fn prepare(&mut self) -> Result<(), M3uFilterError> { let re = regex::Regex::new(&self.group_pattern); if re.is_err() { return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "cant parse regex: {}", &self.group_pattern); @@ -245,7 +245,7 @@ impl ConfigSortChannel { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] -pub(crate) struct ConfigSort { +pub struct ConfigSort { #[serde(default)] pub match_as_ascii: bool, #[serde(default)] @@ -255,7 +255,7 @@ pub(crate) struct ConfigSort { } impl ConfigSort { - pub(crate) fn prepare(&mut self) -> Result<(), M3uFilterError> { + pub fn prepare(&mut self) -> Result<(), M3uFilterError> { if let Some(channels) = self.channels.as_mut() { handle_m3u_filter_error_result_list!(M3uFilterErrorKind::Info, channels.iter_mut().map(ConfigSortChannel::prepare)); } @@ -264,7 +264,7 @@ impl ConfigSort { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub(crate) struct ConfigRename { +pub struct ConfigRename { pub field: ItemField, pub pattern: String, pub new_name: String, @@ -285,7 +285,7 @@ impl ConfigRename { #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] -pub(crate) struct ConfigTargetOptions { +pub struct ConfigTargetOptions { #[serde(default)] pub ignore_logo: bool, #[serde(default)] @@ -307,7 +307,7 @@ pub(crate) struct ConfigTargetOptions { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub(crate) struct TargetOutput { +pub struct TargetOutput { #[serde(alias = "type")] pub target: TargetType, #[serde(skip_serializing_if = "Option::is_none")] @@ -315,7 +315,7 @@ pub(crate) struct TargetOutput { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] -pub(crate) struct ConfigTarget { +pub struct ConfigTarget { #[serde(skip)] pub id: u16, #[serde(default = "default_as_true")] @@ -347,7 +347,7 @@ pub(crate) struct ConfigTarget { impl ConfigTarget { - pub(crate) fn prepare(&mut self, id: u16, templates: Option<&Vec>) -> Result<(), M3uFilterError> { + pub fn prepare(&mut self, id: u16, templates: Option<&Vec>) -> Result<(), M3uFilterError> { self.id = id; if self.output.is_empty() { return Err(M3uFilterError::new(M3uFilterErrorKind::Info, format!("Missing output format for {}", self.name))); @@ -410,21 +410,21 @@ impl ConfigTarget { } } - pub(crate) fn filter(&self, provider: &ValueProvider) -> bool { + pub fn filter(&self, provider: &ValueProvider) -> bool { let mut processor = MockValueProcessor {}; return self.t_filter.as_ref().unwrap().filter(provider, &mut processor); } - pub(crate) fn get_m3u_filename(&self) -> Option<&String> { + pub fn get_m3u_filename(&self) -> Option<&String> { for format in &self.output { - if let TargetType::M3u = format.target { + if format.target == TargetType::M3u { return format.filename.as_ref(); } } None } - pub(crate) fn has_output(&self, tt: &TargetType) -> bool { + pub fn has_output(&self, tt: &TargetType) -> bool { for format in &self.output { if tt.eq(&format.target) { return true; @@ -435,19 +435,19 @@ impl ConfigTarget { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub(crate) struct ConfigSource { +pub struct ConfigSource { pub inputs: Vec, pub targets: Vec, } impl ConfigSource { #[allow(clippy::cast_possible_truncation)] - pub(crate) fn prepare(&mut self, index: u16) -> Result { + pub fn prepare(&mut self, index: u16) -> Result { handle_m3u_filter_error_result_list!(M3uFilterErrorKind::Info, self.inputs.iter_mut().enumerate().map(|(idx, i)| i.prepare(index+(idx as u16)))); Ok(index + (self.inputs.len() as u16)) } - // pub(crate) fn get_inputs_for_target(&self, target_name: &str) -> Option> { + // pub fn get_inputs_for_target(&self, target_name: &str) -> Option> { // for target in &self.targets { // if target.name.eq(target_name) { // let inputs = self.inputs.iter().filter(|&i| i.enabled).collect::>(); @@ -461,13 +461,13 @@ impl ConfigSource { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub(crate) struct InputAffix { +pub struct InputAffix { pub field: String, pub value: String, } -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Sequence, PartialEq, Default)] -pub(crate) enum InputType { +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Sequence, PartialEq, Eq, Default)] +pub enum InputType { #[serde(rename = "m3u")] #[default] M3u, @@ -483,8 +483,8 @@ impl InputType { impl Display for InputType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", match self { - InputType::M3u => Self::M3U, - InputType::Xtream => Self::XTREAM, + Self::M3u => Self::M3U, + Self::Xtream => Self::XTREAM, }) } } @@ -494,9 +494,9 @@ impl FromStr for InputType { fn from_str(s: &str) -> Result { if s.eq("m3u") { - Ok(InputType::M3u) + Ok(Self::M3u) } else if s.eq("xtream") { - Ok(InputType::Xtream) + Ok(Self::Xtream) } else { create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "Unkown InputType: {}", s) } @@ -504,7 +504,7 @@ impl FromStr for InputType { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] -pub(crate) struct ConfigInputOptions { +pub struct ConfigInputOptions { #[serde(default)] pub xtream_skip_live: bool, #[serde(default)] @@ -513,14 +513,14 @@ pub(crate) struct ConfigInputOptions { pub xtream_skip_series: bool, } -pub(crate) struct InputUserInfo { +pub struct InputUserInfo { pub base_url: String, pub username: String, pub password: String, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] -pub(crate) struct ConfigInput { +pub struct ConfigInput { #[serde(skip)] pub id: u16, #[serde(default, rename = "type")] @@ -586,7 +586,7 @@ impl ConfigInput { Ok(()) } - pub(crate) fn get_user_info(&self) -> Option { + pub fn get_user_info(&self) -> Option { if self.input_type == InputType::Xtream { if self.username.is_some() || self.password.is_some() { return Some(InputUserInfo { @@ -619,7 +619,7 @@ impl ConfigInput { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] -pub(crate) struct ConfigApi { +pub struct ConfigApi { pub host: String, pub port: u16, #[serde(default)] @@ -635,18 +635,18 @@ impl ConfigApi { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub(crate) struct TelegramMessagingConfig { +pub struct TelegramMessagingConfig { pub bot_token: String, pub chat_ids: Vec, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub(crate) struct RestMessagingConfig { +pub struct RestMessagingConfig { pub url: String, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] -pub(crate) struct MessagingConfig { +pub struct MessagingConfig { #[serde(default)] pub notify_on: Vec, #[serde(default)] @@ -656,7 +656,7 @@ pub(crate) struct MessagingConfig { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] -pub(crate) struct VideoDownloadConfig { +pub struct VideoDownloadConfig { #[serde(default)] pub headers: HashMap, #[serde(default)] @@ -674,7 +674,7 @@ pub(crate) struct VideoDownloadConfig { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] -pub(crate) struct VideoConfig { +pub struct VideoConfig { #[serde(default)] pub extensions: Vec, #[serde(default, skip_serializing_if = "Option::is_none")] @@ -714,7 +714,7 @@ impl VideoConfig { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] -pub(crate) struct ConfigDto { +pub struct ConfigDto { #[serde(default)] pub threads: u8, pub api: ConfigApi, @@ -753,7 +753,7 @@ impl ConfigDto { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub(crate) struct WebAuthConfig { +pub struct WebAuthConfig { #[serde(default = "default_as_true")] pub enabled: bool, pub issuer: String, @@ -772,10 +772,7 @@ impl WebAuthConfig { self.userfile = Some(config_reader::resolve_env_var(file)); } } - let userfile_name = match &self.userfile { - None => file_utils::get_default_user_file_path(config_path), - Some(file) => file.to_owned() - }; + let userfile_name = self.userfile.as_ref().map_or_else(|| file_utils::get_default_user_file_path(config_path), std::borrow::ToOwned::to_owned); self.userfile = Some(userfile_name.clone()); let mut userfile_path = PathBuf::from(&userfile_name); @@ -820,7 +817,7 @@ impl WebAuthConfig { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] -pub(crate) struct Config { +pub struct Config { #[serde(default)] pub threads: u8, pub api: ConfigApi, @@ -877,7 +874,7 @@ impl Config { } } - // pub(crate) fn get_inputs_for_target(&self, target_name: &str) -> Option> { + // pub fn get_inputs_for_target(&self, target_name: &str) -> Option> { // for source in &self.sources { // if let Some(cfg) = source.get_inputs_for_target(target_name) { // return Some(cfg); @@ -887,24 +884,14 @@ impl Config { // } pub fn get_target_for_user(&self, username: &str, password: &str) -> Option<(ProxyUserCredentials, &ConfigTarget)> { - match self.t_api_proxy.read().unwrap().as_ref() { - Some(api_proxy) => { - self._get_target_for_user(api_proxy.get_target_name(username, password)) - } - _ => None - } + self.t_api_proxy.read().unwrap().as_ref().and_then(|api_proxy| self._get_target_for_user(api_proxy.get_target_name(username, password))) } pub fn get_target_for_user_by_token(&self, token: &str) -> Option<(ProxyUserCredentials, &ConfigTarget)> { - match self.t_api_proxy.read().unwrap().as_ref() { - Some(api_proxy) => { - self._get_target_for_user(api_proxy.get_target_name_by_token(token)) - } - _ => None - } + self.t_api_proxy.read().unwrap().as_ref().and_then(|api_proxy| self._get_target_for_user(api_proxy.get_target_name_by_token(token))) } - pub(crate) fn get_input_by_id(&self, input_id: u16) -> Option<&ConfigInput> { + pub fn get_input_by_id(&self, input_id: u16) -> Option<&ConfigInput> { for source in &self.sources { for input in &source.inputs { if input.id == input_id { @@ -915,7 +902,7 @@ impl Config { None } - pub(crate) fn set_mappings(&mut self, mappings_cfg: &Mappings) { + pub fn set_mappings(&mut self, mappings_cfg: &Mappings) { for source in &mut self.sources { for target in &mut source.targets { if let Some(mapping_ids) = &target.mapping { @@ -1052,7 +1039,7 @@ impl Config { /// * `target_args` the program parameters given with `-target` parameter. /// * `sources` configured sources in config file /// -pub(crate) fn validate_targets(target_args: &Option>, sources: &Vec) -> Result { +pub fn validate_targets(target_args: &Option>, sources: &Vec) -> Result { let mut enabled = true; let mut inputs: Vec = vec![]; let mut targets: Vec = vec![]; @@ -1096,6 +1083,6 @@ pub(crate) fn validate_targets(target_args: &Option>, sources: &Vec< #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub(crate) struct HealthcheckConfig { +pub struct HealthcheckConfig { pub api: ConfigApi } \ No newline at end of file diff --git a/src/model/healthcheck.rs b/src/model/healthcheck.rs index cb99d0884..0531013ad 100644 --- a/src/model/healthcheck.rs +++ b/src/model/healthcheck.rs @@ -1,5 +1,5 @@ #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub(crate) struct Healthcheck { +pub struct Healthcheck { pub status: String, pub version: String, pub time: String, diff --git a/src/model/mapping.rs b/src/model/mapping.rs index af3ed7a9a..d19dc7d39 100644 --- a/src/model/mapping.rs +++ b/src/model/mapping.rs @@ -4,21 +4,21 @@ use std::collections::HashMap; use std::fmt::Display; use std::rc::Rc; use std::str::FromStr; -use std::sync::{Arc, Mutex}; - +use std::sync::{Arc}; +use std::sync::atomic::AtomicU32; use enum_iterator::Sequence; -use log::{trace, debug, error}; +use log::{debug, error, trace}; use regex::Regex; -use crate::{create_m3u_filter_error_result, handle_m3u_filter_error_result, valid_property}; -use crate::filter::{apply_templates_to_pattern, Filter, get_filter, PatternTemplate, prepare_templates, RegexWithCaptures, ValueProcessor}; +use crate::filter::{apply_templates_to_pattern, get_filter, prepare_templates, Filter, PatternTemplate, RegexWithCaptures, ValueProcessor}; use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind}; -use crate::model::config::{AFFIX_FIELDS, COUNTER_FIELDS, ItemField, MAPPER_ATTRIBUTE_FIELDS}; +use crate::model::config::{ItemField, AFFIX_FIELDS, COUNTER_FIELDS, MAPPER_ATTRIBUTE_FIELDS}; use crate::model::playlist::{FieldAccessor, PlaylistItem}; use crate::utils::string_utils::Capitalize; +use crate::{create_m3u_filter_error_result, handle_m3u_filter_error_result, valid_property}; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] -pub(crate) struct MappingTag { +pub struct MappingTag { pub name: String, pub captures: Vec, #[serde(default)] @@ -30,8 +30,8 @@ pub(crate) struct MappingTag { } -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Sequence, PartialEq)] -pub(crate) enum CounterModifier { +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Sequence, PartialEq, Eq)] +pub enum CounterModifier { #[serde(rename = "assign")] Assign, #[serde(rename = "suffix")] @@ -79,7 +79,7 @@ impl FromStr for CounterModifier { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] -pub(crate) struct MappingCounterDefinition { +pub struct MappingCounterDefinition { pub filter: String, pub field: String, #[serde(default)] @@ -91,16 +91,16 @@ pub(crate) struct MappingCounterDefinition { } #[derive(Debug, Clone)] -pub(crate) struct MappingCounter { +pub struct MappingCounter { pub filter: Filter, pub field: String, pub concat: String, pub modifier: CounterModifier, - pub value: Arc>, + pub value: Arc, } -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Sequence, PartialEq)] -pub(crate) enum TransformModifier { +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Sequence, PartialEq, Eq)] +pub enum TransformModifier { #[serde(rename = "lowercase")] Lowercase, #[serde(rename = "uppercase")] @@ -148,7 +148,7 @@ impl FromStr for TransformModifier { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub(crate) struct MapperTransform { +pub struct MapperTransform { pub field: String, pub modifier: TransformModifier, pub pattern: Option, @@ -180,7 +180,7 @@ impl MapperTransform { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] -pub(crate) struct Mapper { +pub struct Mapper { pub filter: Option, pub pattern: String, #[serde(default)] @@ -194,9 +194,9 @@ pub(crate) struct Mapper { #[serde(default)] transform: Option>, #[serde(skip_serializing, skip_deserializing)] - pub(crate) t_filter: Option, + pub t_filter: Option, #[serde(skip_serializing, skip_deserializing)] - pub(crate) t_pattern: Option, + pub t_pattern: Option, #[serde(skip_serializing, skip_deserializing)] t_tags: Vec, #[serde(skip_serializing, skip_deserializing)] @@ -256,10 +256,7 @@ impl Mapper { } _ => self.t_filter = None } - self.t_tags = match tags { - Some(list) => list.clone(), - _ => vec![] - }; + self.t_tags = tags.map_or_else(std::vec::Vec::new, std::clone::Clone::clone); self.t_tagre = Some(Regex::new("").unwrap()); self.t_attre = Some(Regex::new("<(.*?)>").unwrap()); Ok(()) @@ -269,7 +266,7 @@ impl Mapper { } } -pub(crate) struct MappingValueProcessor<'a> { +pub struct MappingValueProcessor<'a> { pub pli: RefCell<&'a PlaylistItem>, pub mapper: &'a Mapper, } @@ -279,14 +276,14 @@ impl MappingValueProcessor<'_> { self.pli.borrow().header.borrow().get_field(key) } - fn set_property(&mut self, key: &str, value: &str) { + fn set_property(&self, key: &str, value: &str) { if !self.pli.borrow().header.borrow_mut().set_field(key, value) { error!("Cant set unknown field {} to {}", key, value); } trace!("Property {} set to {}", key, value); } - fn apply_attributes(&mut self, captured_names: &HashMap<&str, &str>) { + fn apply_attributes(&self, captured_names: &HashMap<&str, &str>) { let mapper = self.mapper; let attr_re = &mapper.t_attre.as_ref().unwrap(); let attributes = &mapper.attributes; @@ -303,7 +300,7 @@ impl MappingValueProcessor<'_> { } } - fn apply_tags(&mut self, value: &String, captures: &HashMap<&str, &str>) -> Option { + fn apply_tags(&self, value: &String, captures: &HashMap<&str, &str>) -> Option { let mut new_value = String::from(value); let tag_captures = self.mapper.t_tagre.as_ref().unwrap().captures_iter(value) .filter(|caps| caps.len() > 1) @@ -341,7 +338,7 @@ impl MappingValueProcessor<'_> { Some(new_value) } - fn apply_suffix(&mut self, captures: &HashMap<&str, &str>) { + fn apply_suffix(&self, captures: &HashMap<&str, &str>) { let mapper = self.mapper; let suffix = &mapper.suffix; @@ -355,7 +352,7 @@ impl MappingValueProcessor<'_> { } } - fn apply_prefix(&mut self, captures: &HashMap<&str, &str>) { + fn apply_prefix(&self, captures: &HashMap<&str, &str>) { let mapper = self.mapper; let prefix = &mapper.prefix; for (key, value) in prefix { @@ -368,7 +365,7 @@ impl MappingValueProcessor<'_> { } } - fn apply_assignments(&mut self) { + fn apply_assignments(&self) { let mapper = self.mapper; let assignments = &mapper.assignments; for (key, value) in assignments { @@ -386,20 +383,16 @@ impl MappingValueProcessor<'_> { } } - fn apply_transform(&mut self) { + fn apply_transform(&self) { let mapper = self.mapper; match &mapper.transform { None => {} Some(transform_list) => { for transform in transform_list { if let Some(prop_value) = self.get_property(&transform.field) { - let value = if let Some(regex) = &transform.t_pattern { - regex.replace_all(&prop_value, |caps: ®ex::Captures| { - Self::apply_transform_modifier(&transform.modifier, &caps[0]) - }) - } else { - Cow::from(Self::apply_transform_modifier(&transform.modifier, prop_value.as_str())) - }; + let value = transform.t_pattern.as_ref().map_or_else(|| Cow::from(Self::apply_transform_modifier(&transform.modifier, prop_value.as_str())), |regex| regex.replace_all(&prop_value, |caps: ®ex::Captures| { + Self::apply_transform_modifier(&transform.modifier, &caps[0]) + })); self.set_property(&transform.field, &value); } } @@ -415,16 +408,16 @@ impl ValueProcessor for MappingValueProcessor<'_> { rewc.re.captures_iter(value) .filter(|caps| caps.len() > 1) .for_each(|captures| - for capture_name in &rewc.captures { - let match_opt = captures.name(capture_name.as_str()); - let capture_value = if match_opt.is_some() { - match_opt.map_or("", |m| m.as_str()) - } else { - "" - }; - debug!("match {}: {}", capture_name, capture_value); - captured_values.insert(capture_name.as_str(), capture_value); - } + for capture_name in &rewc.captures { + let match_opt = captures.name(capture_name.as_str()); + let capture_value = if match_opt.is_some() { + match_opt.map_or("", |m| m.as_str()) + } else { + "" + }; + debug!("match {}: {}", capture_name, capture_value); + captured_values.insert(capture_name.as_str(), capture_value); + } ); } MappingValueProcessor::<'_>::apply_attributes(self, &captured_values); @@ -437,7 +430,7 @@ impl ValueProcessor for MappingValueProcessor<'_> { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] -pub(crate) struct Mapping { +pub struct Mapping { pub id: String, #[serde(default)] pub match_as_ascii: bool, @@ -468,7 +461,7 @@ impl Mapping { field: def.field.clone(), concat: def.concat.clone(), modifier: def.modifier.clone(), - value: Arc::new(Mutex::new(def.value)), + value: Arc::new(AtomicU32::new(def.value)), }); } Err(e) => return Err(M3uFilterError::new(M3uFilterErrorKind::Info, e.to_string())) @@ -482,7 +475,7 @@ impl Mapping { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub(crate) struct MappingDefinition { +pub struct MappingDefinition { pub templates: Option>, pub tags: Option>, pub mapping: Vec, @@ -499,14 +492,8 @@ impl MappingDefinition { } }; for mapping in &mut self.mapping { - let template_list = match &self.templates { - Some(templ) => Some(templ), - _ => None - }; - let tag_list = match &self.tags { - Some(t) => Some(t), - _ => None - }; + let template_list = self.templates.as_ref(); + let tag_list = self.tags.as_ref(); handle_m3u_filter_error_result!(M3uFilterErrorKind::Info, mapping.prepare(template_list, tag_list)); } Ok(()) @@ -514,7 +501,7 @@ impl MappingDefinition { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub(crate) struct Mappings { +pub struct Mappings { pub mappings: MappingDefinition, } diff --git a/src/model/mod.rs b/src/model/mod.rs index e1540a09a..1bc590f8b 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -1,8 +1,8 @@ -pub(crate) mod config; -pub(crate) mod playlist; -pub(crate) mod mapping; -pub(crate) mod api_proxy; -pub(crate) mod stats; -pub(crate) mod xmltv; -pub(crate) mod xtream; -pub(crate) mod healthcheck; \ No newline at end of file +pub mod config; +pub mod playlist; +pub mod mapping; +pub mod api_proxy; +pub mod stats; +pub mod xmltv; +pub mod xtream; +pub mod healthcheck; \ No newline at end of file diff --git a/src/model/playlist.rs b/src/model/playlist.rs index 8f1bfb04e..8e511adbe 100644 --- a/src/model/playlist.rs +++ b/src/model/playlist.rs @@ -17,14 +17,14 @@ use crate::repository::storage::hash_string; // https://siptv.eu/howto/playlist.html #[derive(Debug, Clone)] -pub(crate) struct FetchedPlaylist<'a> { // Contains playlist for one input +pub struct FetchedPlaylist<'a> { // Contains playlist for one input pub input: &'a ConfigInput, pub playlistgroups: Vec, pub epg: Option, } impl FetchedPlaylist<'_> { - pub(crate) fn update_playlist(&mut self, plg: &PlaylistGroup) { + pub fn update_playlist(&mut self, plg: &PlaylistGroup) { for grp in &mut self.playlistgroups { if grp.id == plg.id { plg.channels.iter().for_each(|item| grp.channels.push(item.clone())); @@ -36,7 +36,7 @@ impl FetchedPlaylist<'_> { #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, Default)] #[repr(u8)] -pub(crate) enum XtreamCluster { +pub enum XtreamCluster { #[default] Live = 1, Video = 2, @@ -44,12 +44,11 @@ pub(crate) enum XtreamCluster { } impl XtreamCluster { - - pub fn as_str(&self) -> &str { + pub const fn as_str(&self) -> &str { match self { - XtreamCluster::Live => "Live", - XtreamCluster::Video => "Video", - XtreamCluster::Series => "Series", + Self::Live => "Live", + Self::Video => "Video", + Self::Series => "Series", } } } @@ -64,9 +63,9 @@ impl TryFrom for XtreamCluster { type Error = String; fn try_from(item_type: PlaylistItemType) -> Result { match item_type { - PlaylistItemType::Live => Ok(XtreamCluster::Live), - PlaylistItemType::Video => Ok(XtreamCluster::Video), - PlaylistItemType::Series => Ok(XtreamCluster::Series), + PlaylistItemType::Live => Ok(Self::Live), + PlaylistItemType::Video => Ok(Self::Video), + PlaylistItemType::Series => Ok(Self::Series), _ => Err(format!("Cant convert {item_type}")), } } @@ -74,7 +73,7 @@ impl TryFrom for XtreamCluster { #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, Default)] #[repr(u8)] -pub(crate) enum PlaylistItemType { +pub enum PlaylistItemType { #[default] Live = 1, Video = 2, @@ -87,9 +86,9 @@ pub(crate) enum PlaylistItemType { impl From for PlaylistItemType { fn from(xtream_cluster: XtreamCluster) -> Self { match xtream_cluster { - XtreamCluster::Live => PlaylistItemType::Live, - XtreamCluster::Video => PlaylistItemType::Video, - XtreamCluster::Series => PlaylistItemType::SeriesInfo, + XtreamCluster::Live => Self::Live, + XtreamCluster::Video => Self::Video, + XtreamCluster::Series => Self::SeriesInfo, } } } @@ -106,23 +105,23 @@ impl PlaylistItemType { impl Display for PlaylistItemType { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", match self { - PlaylistItemType::Live => Self::LIVE, - PlaylistItemType::Video => Self::VIDEO, - PlaylistItemType::Series => Self::SERIES, - PlaylistItemType::SeriesInfo => Self::SERIES_INFO, - PlaylistItemType::SeriesEpisode => Self::SERIES_EPISODE, - PlaylistItemType::Catchup => Self::CATCHUP, + Self::Live => Self::LIVE, + Self::Video => Self::VIDEO, + Self::Series => Self::SERIES, + Self::SeriesInfo => Self::SERIES_INFO, + Self::SeriesEpisode => Self::SERIES_EPISODE, + Self::Catchup => Self::CATCHUP, }) } } -pub(crate) trait FieldAccessor { +pub trait FieldAccessor { fn get_field(&self, field: &str) -> Option>; fn set_field(&mut self, field: &str, value: &str) -> bool; } #[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub(crate) struct PlaylistItemHeader { +pub struct PlaylistItemHeader { pub uuid: Rc<[u8; 32]>, // calculated pub id: Rc, // provider id pub virtual_id: u32, // virtual id @@ -151,14 +150,14 @@ pub(crate) struct PlaylistItemHeader { } impl PlaylistItemHeader { - pub(crate) fn gen_uuid(&mut self) { + pub fn gen_uuid(&mut self) { self.uuid = Rc::new(hash_string(&self.url)); } - pub(crate) fn get_uuid(&self) -> &Rc<[u8; 32]> { + pub const fn get_uuid(&self) -> &Rc<[u8; 32]> { &self.uuid } - pub(crate) fn get_provider_id(&mut self) -> Option { + pub fn get_provider_id(&mut self) -> Option { match self.id.parse::() { Ok(id) => Some(id), Err(_) => match extract_id_from_url(&self.url) { @@ -222,7 +221,7 @@ macro_rules! generate_field_accessor_impl_for_playlist_item_header { generate_field_accessor_impl_for_playlist_item_header!(id, /*virtual_id,*/ name, chno, logo, logo_small, group, title, parent_code, audio_track, time_shift, rec, url;); #[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) struct M3uPlaylistItem { +pub struct M3uPlaylistItem { pub virtual_id: u32, pub provider_id: Rc, pub name: Rc, @@ -264,7 +263,7 @@ impl M3uPlaylistItem { } #[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) struct XtreamPlaylistItem { +pub struct XtreamPlaylistItem { pub virtual_id: u32, pub provider_id: u32, pub name: Rc, @@ -291,7 +290,7 @@ impl XtreamPlaylistItem { } #[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) struct PlaylistItem { +pub struct PlaylistItem { pub header: RefCell, } @@ -334,13 +333,7 @@ impl PlaylistItem { url: Rc::clone(&header.url), epg_channel_id: header.epg_channel_id.clone(), xtream_cluster: header.xtream_cluster, - additional_properties: match &header.additional_properties { - None => None, - Some(props) => match serde_json::to_string(props) { - Ok(val) => Some(val), - Err(_) => None - } - }, + additional_properties: header.additional_properties.as_ref().and_then(|props| serde_json::to_string(props).ok()), item_type: header.item_type, series_fetched: header.series_fetched, category_id: header.category_id, @@ -355,7 +348,7 @@ impl PlaylistItem { } #[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) struct PlaylistGroup { +pub struct PlaylistGroup { pub id: u32, pub title: Rc, pub channels: Vec, @@ -364,7 +357,7 @@ pub(crate) struct PlaylistGroup { } impl PlaylistGroup { - pub(crate) fn on_load(&mut self) { + pub fn on_load(&mut self) { self.channels.iter().for_each(|pl| pl.header.borrow_mut().gen_uuid()); } } \ No newline at end of file diff --git a/src/model/stats.rs b/src/model/stats.rs index 12d99a91d..0d6dcdb13 100644 --- a/src/model/stats.rs +++ b/src/model/stats.rs @@ -3,7 +3,7 @@ use std::fmt::Display; use crate::model::config::InputType; #[derive(Debug, Clone)] -pub(crate) struct PlaylistStats { +pub struct PlaylistStats { pub group_count: usize, pub channel_count: usize, } @@ -14,7 +14,7 @@ impl Display for PlaylistStats { } } -pub(crate) fn format_elapsed_time(seconds: u64) -> String { +pub fn format_elapsed_time(seconds: u64) -> String { if seconds < 60 { format!("{seconds} secs") } else { @@ -25,7 +25,7 @@ pub(crate) fn format_elapsed_time(seconds: u64) -> String { } #[derive(Debug, Clone)] -pub(crate) struct InputStats { +pub struct InputStats { pub name: String, pub input_type: InputType, pub error_count: usize, diff --git a/src/model/xmltv.rs b/src/model/xmltv.rs index e3da54b96..5fb6632de 100644 --- a/src/model/xmltv.rs +++ b/src/model/xmltv.rs @@ -5,16 +5,16 @@ use std::rc::Rc; use quick_xml::{Error, Writer}; use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event}; -pub(crate) const EPG_TAG_TV: &str = "tv"; -pub(crate) const EPG_TAG_PROGRAMME: &str = "programme"; -pub(crate) const EPG_TAG_CHANNEL: &str = "channel"; -pub(crate) const EPG_ATTRIB_ID: &str = "id"; -pub(crate) const EPG_ATTRIB_CHANNEL: &str = "channel"; +pub const EPG_TAG_TV: &str = "tv"; +pub const EPG_TAG_PROGRAMME: &str = "programme"; +pub const EPG_TAG_CHANNEL: &str = "channel"; +pub const EPG_ATTRIB_ID: &str = "id"; +pub const EPG_ATTRIB_CHANNEL: &str = "channel"; // https://github.com/XMLTV/xmltv/blob/master/xmltv.dtd #[derive(Debug, Clone)] -pub(crate) struct XmlTag { +pub struct XmlTag { pub name: String, pub value: Option, pub attributes: Option>>, @@ -22,13 +22,8 @@ pub(crate) struct XmlTag { } impl XmlTag { - pub(crate) fn get_attribute_value(&self, attr_name: &str) -> Option<&String> { - match &self.attributes { - None => None, - Some(attr) => { - attr.get(attr_name) - } - } + pub fn get_attribute_value(&self, attr_name: &str) -> Option<&String> { + self.attributes.as_ref().and_then(|attr| attr.get(attr_name)) } fn write_to(&self, writer: &mut Writer) -> Result<(), Error> { @@ -49,13 +44,13 @@ impl XmlTag { #[derive(Debug, Clone)] -pub(crate) struct Epg { +pub struct Epg { pub attributes: Option>>, pub children: Vec, } impl Epg { - pub(crate) fn write_to(&self, writer: &mut Writer) -> Result<(), quick_xml::Error> { + pub fn write_to(&self, writer: &mut Writer) -> Result<(), quick_xml::Error> { let mut elem = BytesStart::new("tv"); if let Some(attribs) = self.attributes.as_ref() { attribs.iter().for_each(|(k, v)| elem.push_attribute((k.as_str(), v.as_str()))); @@ -69,6 +64,6 @@ impl Epg { } #[derive(Debug, Clone)] -pub(crate) struct TVGuide { +pub struct TVGuide { pub file: PathBuf, } diff --git a/src/model/xtream.rs b/src/model/xtream.rs index 2adb4ea4a..cb534edff 100644 --- a/src/model/xtream.rs +++ b/src/model/xtream.rs @@ -2,8 +2,8 @@ use std::collections::HashMap; use std::iter::FromIterator; use std::rc::Rc; -use serde::{Deserialize, Deserializer, Serialize}; use serde::de::DeserializeOwned; +use serde::{Deserialize, Deserializer, Serialize}; use serde_json::{Map, Value}; use crate::model::config::ConfigTargetOptions; @@ -50,10 +50,7 @@ where // (if it is any other string) MaybeNumber::NumberString(string) => { - match serde_json::from_str::(string.as_str()) { - Ok(val) => Ok(Some(val)), - Err(_) => Ok(None) - } + serde_json::from_str::(string.as_str()).map_or_else(|_| Ok(None), |val| Ok(Some(val))) } } } @@ -107,7 +104,7 @@ where } #[derive(Deserialize, Default)] -pub(crate) struct XtreamCategory { +pub struct XtreamCategory { #[serde(deserialize_with = "deserialize_as_rc_string")] pub category_id: Rc, #[serde(deserialize_with = "deserialize_as_rc_string")] @@ -118,13 +115,13 @@ pub(crate) struct XtreamCategory { } impl XtreamCategory { - pub(crate) fn add(&mut self, item: PlaylistItem) { + pub fn add(&mut self, item: PlaylistItem) { self.channels.push(item); } } #[derive(Serialize, Deserialize)] -pub(crate) struct XtreamStream { +pub struct XtreamStream { #[serde(default, deserialize_with = "deserialize_as_rc_string")] pub name: Rc, #[serde(default, deserialize_with = "deserialize_as_rc_string")] @@ -217,11 +214,11 @@ macro_rules! add_i64_property_if_exists { } impl XtreamStream { - pub(crate) fn get_stream_id(&self) -> u32 { - self.stream_id.unwrap_or(self.series_id.unwrap_or(0)) + pub fn get_stream_id(&self) -> u32 { + self.stream_id.unwrap_or_else(|| self.series_id.unwrap_or(0)) } - pub(crate) fn get_additional_properties(&self) -> Option { + pub fn get_additional_properties(&self) -> Option { let mut result = Map::new(); if let Some(bdpath) = self.backdrop_path.as_ref() { if !bdpath.is_empty() { @@ -253,7 +250,7 @@ impl XtreamStream { #[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) struct XtreamSeriesInfoSeason { +pub struct XtreamSeriesInfoSeason { pub air_date: String, pub episode_count: u32, pub id: u32, @@ -268,7 +265,7 @@ pub(crate) struct XtreamSeriesInfoSeason { #[derive(Debug, Clone, Serialize, Deserialize)] #[allow(non_snake_case)] -pub(crate) struct XtreamSeriesInfoInfo { +pub struct XtreamSeriesInfoInfo { name: String, cover: String, plot: String, @@ -286,7 +283,7 @@ pub(crate) struct XtreamSeriesInfoInfo { } #[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) struct XtreamSeriesInfoEpisodeInfo { +pub struct XtreamSeriesInfoEpisodeInfo { pub tmdb_id: u32, pub releasedate: String, pub plot: String, @@ -301,7 +298,7 @@ pub(crate) struct XtreamSeriesInfoEpisodeInfo { } #[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) struct XtreamSeriesInfoEpisode { +pub struct XtreamSeriesInfoEpisode { pub id: String, pub episode_num: u32, pub title: String, @@ -314,7 +311,7 @@ pub(crate) struct XtreamSeriesInfoEpisode { } // impl XtreamSeriesInfoEpisode { -// pub(crate) fn get_id(&self) -> u32 { +// pub fn get_id(&self) -> u32 { // match self.id.parse::() { // Ok(id) => id, // Err(_) => { @@ -326,7 +323,7 @@ pub(crate) struct XtreamSeriesInfoEpisode { // } #[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) struct XtreamSeriesInfo { +pub struct XtreamSeriesInfo { pub seasons: Vec, pub info: XtreamSeriesInfoInfo, pub episodes: HashMap>, @@ -334,7 +331,7 @@ pub(crate) struct XtreamSeriesInfo { impl XtreamSeriesInfoEpisode { - pub(crate) fn get_additional_properties(&self, series_info: &XtreamSeriesInfo) -> Option { + pub fn get_additional_properties(&self, series_info: &XtreamSeriesInfo) -> Option { let mut result = Map::new(); let bdpath = &series_info.info.backdrop_path; if !bdpath.is_empty() { @@ -358,7 +355,7 @@ impl XtreamSeriesInfoEpisode { } } -pub(crate) struct XtreamMappingOptions { +pub struct XtreamMappingOptions { pub skip_live_direct_source: bool, pub skip_video_direct_source: bool, pub skip_series_direct_source: bool, @@ -393,7 +390,7 @@ fn append_release_date(document: &mut serde_json::Map) { document.insert("release_date".to_string(), release_date.clone()); } if !&has_release_date_2 { - document.insert("releaseDate".to_string(), release_date.clone()); + document.insert("releaseDate".to_string(), release_date); } } } @@ -423,7 +420,7 @@ fn append_prepared_series_properties(add_props: Option<&Map>, doc } } -pub(crate) fn xtream_playlistitem_to_document(pli: &XtreamPlaylistItem, options: &XtreamMappingOptions) -> serde_json::Value { +pub fn xtream_playlistitem_to_document(pli: &XtreamPlaylistItem, options: &XtreamMappingOptions) -> serde_json::Value { let stream_id_value = Value::Number(serde_json::Number::from(pli.virtual_id)); let mut document = serde_json::Map::from_iter([ ("category_id".to_string(), Value::String(format!("{}", &pli.category_id))), @@ -444,10 +441,7 @@ pub(crate) fn xtream_playlistitem_to_document(pli: &XtreamPlaylistItem, options: } document.insert("thumbnail".to_string(), Value::String(pli.logo_small.as_ref().clone())); document.insert("custom_sid".to_string(), Value::String(String::new())); - document.insert("epg_channel_id".to_string(), match &pli.epg_channel_id { - None => Value::Null, - Some(epg_id) => Value::String(epg_id.as_ref().clone()) - }); + document.insert("epg_channel_id".to_string(), pli.epg_channel_id.as_ref().map_or(Value::Null, |epg_id| Value::String(epg_id.as_ref().clone()))); } XtreamCluster::Video => { document.insert("stream_id".to_string(), stream_id_value); @@ -463,15 +457,7 @@ pub(crate) fn xtream_playlistitem_to_document(pli: &XtreamPlaylistItem, options: } }; - let props = match &pli.additional_properties { - None => None, - Some(add_props) => { - match serde_json::from_str::>(add_props) { - Ok(p) => Some(p), - Err(_) => None, - } - } - }; + let props = pli.additional_properties.as_ref().and_then(|add_props| serde_json::from_str::>(add_props).ok()); if let Some(ref add_props) = props { for (field_name, field_value) in add_props { diff --git a/src/processing/affix_processor.rs b/src/processing/affix_processor.rs index 3504602b2..c1ab4c84c 100644 --- a/src/processing/affix_processor.rs +++ b/src/processing/affix_processor.rs @@ -1,22 +1,18 @@ -use log::{debug, Level, log_enabled}; -use crate::model::config::{AFFIX_FIELDS, ConfigInput, InputAffix}; +use crate::model::config::{ConfigInput, InputAffix, AFFIX_FIELDS}; use crate::model::playlist::{FetchedPlaylist, FieldAccessor, PlaylistItem}; use crate::valid_property; +use log::{debug, log_enabled, Level}; type AffixProcessor<'a> = Box; fn create_affix_processor(affix: &InputAffix, is_prefix: bool) -> AffixProcessor { Box::new(move |channel: &mut PlaylistItem| { let header = &mut channel.header.borrow_mut(); - let value = if let Some(field_value) = header.get_field(affix.field.as_str()) { - if is_prefix { - format!("{}{}", &affix.value, field_value.as_str()) - } else { - format!("{}{}", field_value.as_str(), &affix.value) - } + let value = header.get_field(affix.field.as_str()).map_or_else(|| String::from(&affix.value), |field_value| if is_prefix { + format!("{}{}", &affix.value, field_value.as_str()) } else { - String::from(&affix.value) - }; + format!("{}{}", field_value.as_str(), &affix.value) + }); if log_enabled!(Level::Debug) { debug!("Applying input {}: {}={}", if is_prefix {"prefix"} else {"suffix"}, &affix.field, &value); } diff --git a/src/processing/m3u_parser.rs b/src/processing/m3u_parser.rs index 5457b3d58..8845665bd 100644 --- a/src/processing/m3u_parser.rs +++ b/src/processing/m3u_parser.rs @@ -103,7 +103,7 @@ fn process_header(input: &ConfigInput, video_suffixes: &[&str], content: &str, u if c.is_none() { break; } - if let ',' = c.unwrap() { + if c.unwrap() == ',' { plih.title = Rc::new(get_value(&mut it)); } else { let token = token_till(&mut it, '=', true); @@ -154,18 +154,14 @@ fn process_header(input: &ConfigInput, video_suffixes: &[&str], content: &str, u plih } -pub(crate) fn extract_id_from_url(url: &str) -> Option { +pub fn extract_id_from_url(url: &str) -> Option { if let Some(filename) = url.split('/').last() { - return if let Some(index) = filename.rfind('.') { - Some(filename[..index].to_string()) - } else { - Some(filename.to_string()) - }; + return filename.rfind('.').map_or_else(|| Some(filename.to_string()), |index| Some(filename[..index].to_string())); } None } -pub(crate) fn consume_m3u<'a, I, F: FnMut(PlaylistItem)>(cfg: &Config, input: &ConfigInput, lines: I, mut visit: F) +pub fn consume_m3u<'a, I, F: FnMut(PlaylistItem)>(cfg: &Config, input: &ConfigInput, lines: I, mut visit: F) where I: Iterator, { @@ -204,7 +200,7 @@ where } } -pub(crate) fn parse_m3u<'a, I>(cfg: &Config, input: &ConfigInput, lines: I) -> Vec +pub fn parse_m3u<'a, I>(cfg: &Config, input: &ConfigInput, lines: I) -> Vec where I: Iterator, { @@ -226,7 +222,7 @@ where } }); let mut grp_id = 0; - let result: Vec = sort_order.drain(..).map(|channels| { + let result: Vec = sort_order.into_iter().map(|channels| { // create a group based on the first playlist item let channel = channels.first(); let (cluster, group_title) = channel.map(|pli| diff --git a/src/processing/mod.rs b/src/processing/mod.rs index cda7b468a..52cb7fd0c 100644 --- a/src/processing/mod.rs +++ b/src/processing/mod.rs @@ -1,7 +1,7 @@ -pub(crate) mod m3u_parser; -pub(crate) mod xtream_parser; -pub(crate) mod playlist_processor; -pub(crate) mod xmltv_parser; +pub mod m3u_parser; +pub mod xtream_parser; +pub mod playlist_processor; +pub mod xmltv_parser; mod playlist_watch; mod xtream_processor; mod affix_processor; \ No newline at end of file diff --git a/src/processing/playlist_processor.rs b/src/processing/playlist_processor.rs index 074baedee..f2f3cfdaa 100644 --- a/src/processing/playlist_processor.rs +++ b/src/processing/playlist_processor.rs @@ -8,17 +8,16 @@ use std::sync::{Arc, Mutex}; use std::thread; use actix_rt::System; -use log::{trace, debug, error, info, Level, log_enabled}; +use log::{debug, error, info, log_enabled, trace, Level}; use std::time::Instant; use unidecode::unidecode; -use crate::{Config, get_errors_notify_message, model::config}; -use crate::filter::{get_field_value, MockValueProcessor, set_field_value, ValueProvider}; +use crate::filter::{get_field_value, set_field_value, MockValueProcessor, ValueProvider}; use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind}; -use crate::messaging::{MsgKind, send_message}; +use crate::messaging::{send_message, MsgKind}; use crate::model::config::{ConfigSortChannel, ConfigSortGroup, ConfigTarget, InputType, - ItemField, ProcessingOrder, ProcessTargets, SortOrder::{Asc, Desc}}; + ItemField, ProcessTargets, ProcessingOrder, SortOrder::{Asc, Desc}}; use crate::model::mapping::{CounterModifier, Mapping, MappingValueProcessor}; use crate::model::playlist::{FetchedPlaylist, FieldAccessor, PlaylistGroup, PlaylistItem, XtreamCluster}; use crate::model::stats::{InputStats, PlaylistStats}; @@ -29,6 +28,7 @@ use crate::processing::xtream_processor::playlist_resolve_series; use crate::repository::playlist_repository::persist_playlist; use crate::utils::default_utils::default_as_default; use crate::utils::download; +use crate::{get_errors_notify_message, model::config, Config}; fn is_valid(pli: &PlaylistItem, target: &ConfigTarget) -> bool { let provider = ValueProvider { pli: RefCell::new(pli) }; @@ -70,7 +70,13 @@ fn playlistitem_comparator(a: &PlaylistItem, b: &PlaylistItem, channel_sort: &Co let raw_value_b = get_field_value(b, &channel_sort.field); let value_a = if match_as_ascii { Rc::new(unidecode(&raw_value_a)) } else { raw_value_a }; let value_b = if match_as_ascii { Rc::new(unidecode(&raw_value_b)) } else { raw_value_b }; - if let Some(custom_order) = &channel_sort.sequence { + channel_sort.sequence.as_ref().map_or_else(|| { + let ordering = value_a.partial_cmp(&value_b).unwrap(); + match channel_sort.order { + Asc => ordering, + Desc => ordering.reverse() + } + }, |custom_order| { // Check indices in the custom order vector let index_a = custom_order.iter().position(|s| s == value_a.as_ref()); let index_b = custom_order.iter().position(|s| s == value_b.as_ref()); @@ -97,13 +103,7 @@ fn playlistitem_comparator(a: &PlaylistItem, b: &PlaylistItem, channel_sort: &Co } } } - } else { - let ordering = value_a.partial_cmp(&value_b).unwrap(); - match channel_sort.order { - Asc => ordering, - Desc => ordering.reverse() - } - } + }) } fn sort_playlist(target: &ConfigTarget, new_playlist: &mut [PlaylistGroup]) { @@ -126,7 +126,7 @@ fn sort_playlist(target: &ConfigTarget, new_playlist: &mut [PlaylistGroup]) { } } -fn exec_rename(pli: &mut PlaylistItem, rename: &Option>) { +fn exec_rename(pli: &PlaylistItem, rename: &Option>) { if let Some(renames) = rename { if !renames.is_empty() { let result = pli; @@ -151,7 +151,7 @@ fn rename_playlist(playlist: &mut [PlaylistGroup], target: &ConfigTarget) -> Opt for g in playlist { let mut grp = g.clone(); for r in renames { - if let ItemField::Group = r.field { + if matches!(r.field, ItemField::Group) { let cap = r.re.as_ref().unwrap().replace_all(&grp.title, &r.new_name); if log_enabled!(Level::Debug) { debug!("Renamed group {} to {} for {}", &grp.title, cap, target.name); @@ -211,7 +211,7 @@ fn map_playlist(playlist: &mut [PlaylistGroup], target: &ConfigTarget) -> Option let mut grp = playlist_group.clone(); let mappings = target.t_mapping.as_ref().unwrap(); mappings.iter().filter(|&mapping| !mapping.mapper.is_empty()).for_each(|mapping| - grp.channels = grp.channels.drain(..).map(|chan| map_channel(chan, mapping)).collect()); + grp.channels = grp.channels.drain(..).map(|chan| map_channel(chan, mapping)).collect()); grp }).collect(); @@ -249,7 +249,7 @@ fn map_playlist_counter(target: &ConfigTarget, playlist: &[PlaylistGroup]) { for mapping in mappings { if let Some(counter_list) = &mapping.t_counter { for counter in counter_list { - let mut cntval = counter.value.lock().unwrap(); + let cntval = counter.value.load(core::sync::atomic::Ordering::Relaxed); for plg in playlist { for channel in &plg.channels { let provider = ValueProvider { pli: RefCell::new(channel) }; @@ -257,10 +257,7 @@ fn map_playlist_counter(target: &ConfigTarget, playlist: &[PlaylistGroup]) { let new_value = if counter.modifier == CounterModifier::Assign { cntval.to_string() } else { - let value = match channel.header.borrow_mut().get_field(&counter.field) { - Some(field_value) => field_value.to_string(), - None => String::new(), - }; + let value = channel.header.borrow_mut().get_field(&counter.field).map_or_else(String::new, |field_value| field_value.to_string()); if counter.modifier == CounterModifier::Suffix { format!("{value}{}{cntval}", counter.concat) } else { @@ -268,7 +265,7 @@ fn map_playlist_counter(target: &ConfigTarget, playlist: &[PlaylistGroup]) { } }; channel.header.borrow_mut().set_field(&counter.field, new_value.as_str()); - *cntval += 1; + counter.value.fetch_add(1, core::sync::atomic::Ordering::Relaxed); } } } @@ -315,10 +312,7 @@ async fn process_source(cfg: Arc, source_idx: usize, user_targets: Arc

name_val.as_str(), - None => input.url.as_str(), - }; + let input_name = input.name.as_ref().map_or(input.url.as_str(), |name_val| name_val.as_str()); let group_count = playlistgroups.len(); let channel_count = playlistgroups.iter() .map(|group| group.channels.len()) @@ -375,7 +369,7 @@ fn create_input_stat(group_count: usize, channel_count: usize, error_count: usiz group_count: 0, channel_count: 0, }, - secs_took + secs_took, } } @@ -432,7 +426,7 @@ fn get_processing_pipe(target: &ConfigTarget) -> ProcessingPipe { } -fn execute_pipe<'a>(target: &ConfigTarget, pipe: &ProcessingPipe, fpl: &mut FetchedPlaylist<'a>) -> FetchedPlaylist<'a> { +fn execute_pipe<'a>(target: &ConfigTarget, pipe: &ProcessingPipe, fpl: &FetchedPlaylist<'a>) -> FetchedPlaylist<'a> { let mut new_fpl = FetchedPlaylist { input: fpl.input, playlistgroups: fpl.playlistgroups.clone(), // we need to clone, because of multiple target definitions, we cant change the initial playlist. @@ -448,11 +442,11 @@ fn execute_pipe<'a>(target: &ConfigTarget, pipe: &ProcessingPipe, fpl: &mut Fetc // This method is needed, because of duplicate group names in different inputs. // We merge the same group names considering cluster together. -fn flatten_groups(mut playlistgroups: Vec) -> Vec { +fn flatten_groups(playlistgroups: Vec) -> Vec { let mut sort_order: Vec = vec![]; let mut idx: usize = 0; let mut group_map: HashMap<(Rc, XtreamCluster), usize> = HashMap::new(); - playlistgroups.drain(..).for_each(|group| { + for group in playlistgroups { let key = (Rc::clone(&group.title), group.xtream_cluster); match group_map.entry(key) { std::collections::hash_map::Entry::Vacant(v) => { @@ -464,7 +458,7 @@ fn flatten_groups(mut playlistgroups: Vec) -> Vec sort_order.get_mut(*o.get()).unwrap().channels.extend(group.channels); } }; - }); + } sort_order } @@ -500,7 +494,7 @@ async fn process_playlist<'a>(playlists: &mut [FetchedPlaylist<'a>], // each fetched playlist can have its own epgl url. // we need to process each input epg. - new_fetched_playlists.drain(..).for_each(|mut fp| { + for mut fp in new_fetched_playlists { // collect all epg_channel ids let epg_channel_ids: HashSet<_> = fp.playlistgroups.iter().flat_map(|g| &g.channels) .filter_map(|c| c.header.borrow().epg_channel_id.clone()).collect(); @@ -516,7 +510,7 @@ async fn process_playlist<'a>(playlists: &mut [FetchedPlaylist<'a>], } else if log_enabled!(Level::Debug) { debug!("channel ids are empty"); } - }); + } if new_playlist.is_empty() { info!("Playlist is empty: {}", &target.name); @@ -545,7 +539,7 @@ fn process_watch(target: &ConfigTarget, cfg: &Config, new_playlist: &Vec, targets: Arc) { +pub async fn exec_processing(cfg: Arc, targets: Arc) { let (stats, errors) = process_sources(cfg.clone(), targets.clone()).await; let stats_msg = format!("{{\"stats\": {}}}", stats.iter().map(std::string::ToString::to_string).collect::>().join("\n")); // print stats diff --git a/src/processing/playlist_watch.rs b/src/processing/playlist_watch.rs index 98081390f..082bddc4f 100644 --- a/src/processing/playlist_watch.rs +++ b/src/processing/playlist_watch.rs @@ -7,7 +7,7 @@ use crate::model::playlist::PlaylistGroup; use crate::utils::file_utils; use crate::utils::file_utils::sanitize_filename; -pub(crate) fn process_group_watch(cfg: &Config, target_name: &str, pl: &PlaylistGroup) { +pub fn process_group_watch(cfg: &Config, target_name: &str, pl: &PlaylistGroup) { let mut new_tree = BTreeSet::new(); pl.channels.iter().for_each(|chan| { let header = chan.header.borrow(); @@ -76,13 +76,10 @@ fn handle_watch_notification(cfg: &Config, added: &BTreeSet, removed: &B } fn load_watch_tree(path: &Path) -> Option> { - match std::fs::read(path) { - Ok(encoded) => { + std::fs::read(path).map_or(None, |encoded| { let decoded: BTreeSet = bincode::deserialize(&encoded[..]).unwrap(); Some(decoded) - } - Err(_) => None, - } + }) } fn save_watch_tree(path: &Path, tree: &BTreeSet) -> std::io::Result<()> { diff --git a/src/processing/xmltv_parser.rs b/src/processing/xmltv_parser.rs index 15825ba31..5f3410b0f 100644 --- a/src/processing/xmltv_parser.rs +++ b/src/processing/xmltv_parser.rs @@ -8,7 +8,7 @@ use crate::model::xmltv::{Epg, EPG_ATTRIB_CHANNEL, EPG_ATTRIB_ID, EPG_TAG_TV, EP use crate::utils::compressed_file_reader::CompressedFileReader; impl TVGuide { - pub(crate) fn filter(&self, channel_ids: &HashSet>) -> Option { + pub fn filter(&self, channel_ids: &HashSet>) -> Option { if channel_ids.is_empty() { return None; } @@ -19,16 +19,10 @@ impl TVGuide { let mut filter_tags = |tag: XmlTag| { if match tag.name.as_str() { EPG_TAG_CHANNEL => { - match tag.get_attribute_value(EPG_ATTRIB_ID) { - None => false, - Some(val) => channel_ids.contains(val) - } + tag.get_attribute_value(EPG_ATTRIB_ID).map_or(false, |val| channel_ids.contains(val)) } EPG_TAG_PROGRAMME => { - match tag.get_attribute_value(EPG_ATTRIB_CHANNEL) { - None => false, - Some(val) => channel_ids.contains(val) - } + tag.get_attribute_value(EPG_ATTRIB_CHANNEL).map_or(false, |val| channel_ids.contains(val)) }, EPG_TAG_TV => { tv_attributes.clone_from(&tag.attributes); @@ -55,7 +49,7 @@ impl TVGuide { } } -pub(crate) fn parse_tvguide(content: R, callback: &mut F) +pub fn parse_tvguide(content: R, callback: &mut F) where R: std::io::BufRead, F: FnMut(XmlTag), @@ -72,7 +66,7 @@ where let attributes = e.attributes().filter_map(Result::ok) .filter_map(|a| { let key = String::from_utf8_lossy(a.key.as_ref()).to_string(); - let value = String::from(a.unescape_value().unwrap().as_ref()).to_string(); + let value = String::from(a.unescape_value().unwrap().as_ref()); if value.is_empty() { None } else { @@ -111,8 +105,7 @@ where if let Some(old_tag) = stack.pop().map(|mut r| { let rc_tag = Rc::new(tag); r.children = Some( - r.children.map_or(vec![rc_tag.clone()], - |mut c| { + r.children.map_or_else(|| vec![rc_tag.clone()], |mut c| { c.push(rc_tag.clone()); c })); @@ -135,7 +128,7 @@ where } } -pub(crate) fn flatten_tvguide(tv_guides: &[Epg]) -> Option { +pub fn flatten_tvguide(tv_guides: &[Epg]) -> Option { if tv_guides.is_empty() { None } else { diff --git a/src/processing/xtream_parser.rs b/src/processing/xtream_parser.rs index 96246bfbe..49ea8dc73 100644 --- a/src/processing/xtream_parser.rs +++ b/src/processing/xtream_parser.rs @@ -38,7 +38,7 @@ fn create_xtream_series_info_url(url: &str, username: &str, password: &str, epis } } -pub(crate) fn parse_xtream_series_info(info: &Value, group_title: &str, input: &ConfigInput) -> Result>, M3uFilterError> { +pub fn parse_xtream_series_info(info: &Value, group_title: &str, input: &ConfigInput) -> Result>, M3uFilterError> { let url = input.url.as_str(); let username = input.username.as_ref().map_or("", |v| v); let password = input.password.as_ref().map_or("", |v| v); @@ -87,12 +87,12 @@ fn create_xtream_url(xtream_cluster: XtreamCluster, url: &str, username: &str, p Rc::clone(&stream.direct_source) } } -pub(crate) fn parse_xtream(input: &ConfigInput, +pub fn parse_xtream(input: &ConfigInput, xtream_cluster: XtreamCluster, categories: &Value, streams: &Value) -> Result>, M3uFilterError> { match map_to_xtream_category(categories) { - Ok(mut xtream_categories) => { + Ok(xtream_categories) => { let input_id = input.id; let url = input.url.as_str(); let username = input.username.as_ref().map_or("", |v| v); @@ -101,7 +101,7 @@ pub(crate) fn parse_xtream(input: &ConfigInput, return match map_to_xtream_streams(xtream_cluster, streams) { Ok(xtream_streams) => { let group_map: HashMap::, RefCell> = - xtream_categories.drain(..).map(|category| + xtream_categories.into_iter().map(|category| (Rc::clone(&category.category_id), RefCell::new(category)) ).collect(); diff --git a/src/repository/bplustree.rs b/src/repository/bplustree.rs index 3f205aefa..b1158df57 100644 --- a/src/repository/bplustree.rs +++ b/src/repository/bplustree.rs @@ -86,8 +86,8 @@ where V: Serialize + for<'de> Deserialize<'de> + Clone, { #[inline] - fn new(is_leaf: bool) -> Self { - BPlusTreeNode { + const fn new(is_leaf: bool) -> Self { + Self { is_leaf, keys: vec![], children: vec![], @@ -101,11 +101,11 @@ where } #[inline] - fn get_median_index(order: usize) -> usize { + const fn get_median_index(order: usize) -> usize { order >> 1 } - fn find_leaf_entry(node: &BPlusTreeNode) -> &K { + fn find_leaf_entry(node: &Self) -> &K { if node.is_leaf { node.keys.first().unwrap() } else { @@ -117,10 +117,7 @@ where #[allow(dead_code)] fn query(&self, key: &K) -> Option<&V> { if self.is_leaf { - return match self.keys.binary_search(key) { - Ok(idx) => self.values.get(idx), - Err(_) => None, - }; + return self.keys.binary_search(key).map_or(None, |idx| self.values.get(idx)); } let node = self.children.get(self.get_entry_index_upper_bound(key)).unwrap(); node.query(key) @@ -149,7 +146,7 @@ where get_entry_index_upper_bound::(&self.keys, key) } - fn insert(&mut self, key: K, v: V, inner_order: usize, leaf_order: usize) -> Option> { + fn insert(&mut self, key: K, v: V, inner_order: usize, leaf_order: usize) -> Option { if self.is_leaf { if let Ok(pos) = self.keys.binary_search(&key) { self.values[pos] = v; @@ -184,15 +181,15 @@ where None } - fn split(&mut self, order: usize) -> BPlusTreeNode { - let median = BPlusTreeNode::::get_median_index(order); + fn split(&mut self, order: usize) -> Self { + let median = Self::get_median_index(order); if self.is_leaf { - let mut node = BPlusTreeNode::new(true); + let mut node = Self::new(true); node.keys = self.keys.split_off(median); node.values = self.values.split_off(median); node } else { - let mut node = BPlusTreeNode::new(false); + let mut node = Self::new(false); node.keys = self.keys.split_off(median + 1); node.children = self.children.split_off(median + 1); self.children.push(node.children.first().unwrap().clone()); @@ -200,7 +197,7 @@ where } } - pub(crate) fn traverse(&self, visit: &mut F) + pub fn traverse(&self, visit: &mut F) where F: FnMut(&Vec, &Vec), { @@ -315,10 +312,10 @@ where read_pos += LEN_SIZE; let pointers: Vec = bincode_deserialize(&buffer[read_pos..read_pos + pointers_length])?; if nested { - let nodes: Result>, io::Error> = pointers + let nodes: Result, io::Error> = pointers .iter() .map(|pointer| { - BPlusTreeNode::::deserialize_from_block(file, buffer, *pointer, nested) + Self::deserialize_from_block(file, buffer, *pointer, nested) .map(|(node, _)| node) .map_err(|err| io::Error::new(io::ErrorKind::Other, err.to_string())) }) @@ -330,19 +327,19 @@ where } }; - Ok((BPlusTreeNode { keys, children, is_leaf, values }, children_pointer)) + Ok((Self { keys, children, is_leaf, values }, children_pointer)) } } #[derive(Serialize, Deserialize, Debug, Clone)] -pub(crate) struct BPlusTree { +pub struct BPlusTree { root: BPlusTreeNode, inner_order: usize, leaf_order: usize, dirty: bool, } -fn calc_order() -> (usize, usize) { +const fn calc_order() -> (usize, usize) { let key_size = size_of::() + POINTER_SIZE + size_of::() + BINCODE_OVERHEAD; let inner_order = BLOCK_SIZE / key_size; let leaf_order = BLOCK_SIZE / (key_size + size_of::() + BINCODE_OVERHEAD); @@ -354,9 +351,9 @@ where K: Ord + Serialize + for<'de> Deserialize<'de> + Clone, V: Serialize + for<'de> Deserialize<'de> + Clone, { - pub(crate) fn new() -> Self { + pub const fn new() -> Self { let (inner_order, leaf_order) = calc_order::(); - BPlusTree { + Self { root: BPlusTreeNode::::new(true), inner_order, leaf_order, @@ -364,9 +361,9 @@ where } } - fn new_with_root(root: BPlusTreeNode::) -> Self { + const fn new_with_root(root: BPlusTreeNode::) -> Self { let (inner_order, leaf_order) = calc_order::(); - BPlusTree { + Self { root, inner_order, leaf_order, @@ -374,7 +371,7 @@ where } } - pub(crate) fn insert(&mut self, key: K, value: V) { + pub fn insert(&mut self, key: K, value: V) { self.dirty= true; if self.root.keys.is_empty() { self.root.keys.push(key); @@ -399,11 +396,11 @@ where } #[allow(dead_code)] - pub(crate) fn query(&self, key: &K) -> Option<&V> { + pub fn query(&self, key: &K) -> Option<&V> { self.root.query(key) } - pub(crate) fn store(&mut self, filepath: &Path) -> io::Result { + pub fn store(&mut self, filepath: &Path) -> io::Result { if self.dirty { let mut file = BufWriter::new(OpenOptions::new().write(true).create(true).truncate(true).open(filepath)?); let mut buffer = vec![0u8; BLOCK_SIZE]; @@ -416,15 +413,15 @@ where } } - pub(crate) fn load(filepath: &Path) -> io::Result { + pub fn load(filepath: &Path) -> io::Result { let file = is_file_valid(File::open(filepath)?)?; let mut reader = BufReader::new(file); let mut buffer = vec![0u8; BLOCK_SIZE]; let (root, _) = BPlusTreeNode::deserialize_from_block(&mut reader, &mut buffer, 0, true)?; - Ok(BPlusTree::new_with_root(root)) + Ok(Self::new_with_root(root)) } - pub(crate) fn traverse(&self, mut visit: F) + pub fn traverse(&self, mut visit: F) where F: FnMut(&Vec, &Vec), { @@ -459,7 +456,7 @@ where } } -pub(crate) struct BPlusTreeQuery { +pub struct BPlusTreeQuery { file: BufReader, _marker_k: PhantomData, _marker_v: PhantomData, @@ -470,21 +467,21 @@ where K: Ord + Serialize + for<'de> Deserialize<'de> + Clone, V: Serialize + for<'de> Deserialize<'de> + Clone, { - pub(crate) fn try_new(filepath: &Path) -> io::Result { + pub fn try_new(filepath: &Path) -> io::Result { let file = is_file_valid(File::open(filepath)?)?; - Ok(BPlusTreeQuery { + Ok(Self { file: BufReader::new(file), _marker_k: PhantomData, _marker_v: PhantomData, }) } - pub(crate) fn query(&mut self, key: &K) -> Option { + pub fn query(&mut self, key: &K) -> Option { query_tree(&mut self.file, key) } } -pub(crate) struct BPlusTreeUpdate { +pub struct BPlusTreeUpdate { file: File, _marker_k: PhantomData, _marker_v: PhantomData, @@ -495,7 +492,7 @@ where K: Ord + Serialize + for<'de> Deserialize<'de> + Clone, V: Serialize + for<'de> Deserialize<'de> + Clone, { - pub(crate) fn try_new(filepath: &Path) -> io::Result { + pub fn try_new(filepath: &Path) -> io::Result { if !filepath.exists() { return Err(io::Error::new(io::ErrorKind::NotFound, format!("File not found {}", filepath.to_str().unwrap_or("?")))); } @@ -503,14 +500,14 @@ where .write(true) .read(true) .open(filepath)?)?; - Ok(BPlusTreeUpdate { + Ok(Self { file, _marker_k: PhantomData, _marker_v: PhantomData, }) } - pub(crate) fn query(&mut self, key: &K) -> Option { + pub fn query(&mut self, key: &K) -> Option { let mut reader = BufReader::new(&mut self.file); query_tree(&mut reader, key) } @@ -522,7 +519,7 @@ where result } - pub(crate) fn update(&mut self, key: &K, value: V) -> io::Result { + pub fn update(&mut self, key: &K, value: V) -> io::Result { let mut offset = 0; let mut buffer = vec![0u8; BLOCK_SIZE]; let mut reader = BufReader::new(&mut self.file); diff --git a/src/repository/epg_repository.rs b/src/repository/epg_repository.rs index 806e902d1..02fca5d9d 100644 --- a/src/repository/epg_repository.rs +++ b/src/repository/epg_repository.rs @@ -42,7 +42,7 @@ fn epg_write_file(target: &ConfigTarget, epg: &Epg, path: &Path) -> Result<(), M Ok(()) } -pub(crate) fn epg_write(target: &ConfigTarget, cfg: &Config, target_path: &Path, epg: Option<&Epg>, output: &TargetOutput) -> Result<(), M3uFilterError> { +pub fn epg_write(target: &ConfigTarget, cfg: &Config, target_path: &Path, epg: Option<&Epg>, output: &TargetOutput) -> Result<(), M3uFilterError> { if let Some(epg_data) = epg { match &output.target { TargetType::M3u => { diff --git a/src/repository/id_mapping.rs b/src/repository/id_mapping.rs index a64199b0a..c28aea37e 100644 --- a/src/repository/id_mapping.rs +++ b/src/repository/id_mapping.rs @@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize}; use crate::repository::bplustree::BPlusTree; -pub(crate) struct IdMapping +pub struct IdMapping where V: Serialize + for<'de> Deserialize<'de> + Clone, { @@ -23,7 +23,7 @@ impl IdMapping where V: Serialize + for<'de> Deserialize<'de> + Clone, { - pub(crate) fn new(path: &Path) -> Self { + pub fn new(path: &Path) -> Self { let tree: BPlusTree = match BPlusTree::::deserialize(&path) { Ok(tree) => tree, _ => BPlusTree::::new() @@ -47,12 +47,12 @@ where } } - pub(crate) fn insert(&mut self, id: u32, value: V) { + pub fn insert(&mut self, id: u32, value: V) { self.dirty = true; self.tree.insert(id, value); } - pub(crate) fn persist(&mut self) -> Result<(), Error> { + pub fn persist(&mut self) -> Result<(), Error> { if self.dirty { self.tree.serialize(&self.path)?; } @@ -60,11 +60,11 @@ where Ok(()) } - pub(crate) fn max_id(&self) -> u32 { + pub fn max_id(&self) -> u32 { self.max_id } - pub(crate) fn query(&self, id: u32) -> Option<&V> { + pub fn query(&self, id: u32) -> Option<&V> { self.tree.query(&id) } } diff --git a/src/repository/indexed_document.rs b/src/repository/indexed_document.rs index a508968fb..71471b735 100644 --- a/src/repository/indexed_document.rs +++ b/src/repository/indexed_document.rs @@ -41,10 +41,7 @@ impl IndexedDocument { pub(in crate::repository) fn get_offset(index_path: &Path, doc_id: u32) -> Result { match BPlusTreeQuery::::try_new(index_path) { Ok(mut tree) => { - match tree.query(&doc_id) { - Some(offset) => Ok(u64::from(offset)), - None => Err(Error::new(ErrorKind::NotFound, format!("doc_id not found {doc_id}"))), - } + tree.query(&doc_id).map_or_else(|| Err(Error::new(ErrorKind::NotFound, format!("doc_id not found {doc_id}"))), |offset| Ok(u64::from(offset))) } Err(err) => Err(err) } @@ -160,8 +157,8 @@ impl IndexedDocumentWriter { let size = IndexedDocument::read_content_size(&mut self.main_file)?; if size == encoded_bytes.len() { // check if it is equal - let mut record_buffer = Vec::::with_capacity(size); - self.main_file.read_exact(&mut record_buffer)?; + let mut record_buffer = vec![0; size]; + record_buffer.resize(size, 0); self.main_file.read_exact(&mut record_buffer)?; if record_buffer == encoded_bytes { return Ok(()); } @@ -226,7 +223,7 @@ pub(in crate::repository) struct IndexedDocumentReader { } impl IndexedDocumentReader { - pub fn new(main_path: &Path, index_path: &Path) -> Result, Error> { + pub fn new(main_path: &Path, index_path: &Path) -> Result { if main_path.exists() && index_path.exists() { let mut offsets = Vec::::new(); { @@ -261,7 +258,7 @@ impl IndexedDocumentReader { &self.main_path } - pub fn has_error(&self) -> bool { + pub const fn has_error(&self) -> bool { self.failed } diff --git a/src/repository/kodi_repository.rs b/src/repository/kodi_repository.rs index 9ff9f94f2..d64d3906b 100644 --- a/src/repository/kodi_repository.rs +++ b/src/repository/kodi_repository.rs @@ -39,27 +39,21 @@ fn kodi_style_rename_year(name: &String, style: &KodiStyle) -> (String, Option (String, Option) { - match style.season.find(name) { - Some(m) => { + style.season.find(name).map_or_else(|| (String::from(name), Some(String::from("01"))), |m| { let s_season = &name[m.start()..m.end()]; let season = Some(String::from(&s_season[1..])); let new_name = format!("{}{}", &name[0..m.start()], &name[m.end()..]); (new_name, season) - } - _ => (String::from(name), Some(String::from("01"))), - } + }) } fn kodi_style_rename_episode(name: &String, style: &KodiStyle) -> (String, Option) { - match style.episode.find(name) { - Some(m) => { + style.episode.find(name).map_or_else(|| (String::from(name), None), |m| { let s_episode = &name[m.start()..m.end()]; let episode = Some(String::from(&s_episode[1..])); let new_name = format!("{}{}", &name[0..m.start()], &name[m.end()..]); (new_name, episode) - } - _ => (String::from(name), None), - } + }) } fn kodi_style_rename(name: &String, style: &KodiStyle) -> String { @@ -74,7 +68,7 @@ fn kodi_style_rename(name: &String, style: &KodiStyle) -> String { } -pub(crate) fn kodi_write_strm_playlist(target: &ConfigTarget, cfg: &Config, new_playlist: &[PlaylistGroup], filename: &Option) -> Result<(), M3uFilterError> { +pub fn kodi_write_strm_playlist(target: &ConfigTarget, cfg: &Config, new_playlist: &[PlaylistGroup], filename: &Option) -> Result<(), M3uFilterError> { if !new_playlist.is_empty() { if filename.is_none() { return Err(M3uFilterError::new(M3uFilterErrorKind::Notify, "write strm playlist failed: ".to_string())); diff --git a/src/repository/m3u_playlist_iterator.rs b/src/repository/m3u_playlist_iterator.rs index c5840c763..dfb057dc0 100644 --- a/src/repository/m3u_playlist_iterator.rs +++ b/src/repository/m3u_playlist_iterator.rs @@ -8,7 +8,7 @@ use crate::repository::m3u_repository::m3u_get_file_paths; use crate::repository::storage::ensure_target_storage_path; use crate::utils::file_lock_manager::FileReadGuard; -pub(crate) struct M3uPlaylistIterator { +pub struct M3uPlaylistIterator { reader: IndexedDocumentReader, base_url: String, target_options: Option, diff --git a/src/repository/m3u_repository.rs b/src/repository/m3u_repository.rs index 0abd52110..9782fa834 100644 --- a/src/repository/m3u_repository.rs +++ b/src/repository/m3u_repository.rs @@ -20,13 +20,13 @@ macro_rules! cant_write_result { } } -pub(crate) fn m3u_get_file_paths(target_path: &Path) -> (PathBuf, PathBuf) { +pub fn m3u_get_file_paths(target_path: &Path) -> (PathBuf, PathBuf) { let m3u_path = target_path.join(PathBuf::from(format!("{FILE_M3U}.{FILE_SUFFIX_DB}"))); let index_path = target_path.join(PathBuf::from(format!("{FILE_M3U}.{FILE_SUFFIX_INDEX}"))); (m3u_path, index_path) } -pub(crate) fn m3u_get_epg_file_path(target_path: &Path) -> PathBuf { +pub fn m3u_get_epg_file_path(target_path: &Path) -> PathBuf { let path = target_path.join(PathBuf::from(format!("{FILE_M3U}.{FILE_SUFFIX_DB}"))); file_utils::add_prefix_to_filename(&path, "epg_", Some("xml")) } @@ -37,10 +37,10 @@ fn persist_m3u_playlist_as_text(target: &ConfigTarget, cfg: &Config, m3u_playlis match File::create(&m3u_filename) { Ok(file) => { let mut buf_writer = BufWriter::new(file); - let _ = buf_writer.write("#EXTM3U\n".as_bytes()); + let _ = buf_writer.write(b"#EXTM3U\n"); for m3u in m3u_playlist { let _ = buf_writer.write(m3u.to_m3u(&target.options, None).as_bytes()); - let _ = buf_writer.write("\n".as_bytes()); + let _ = buf_writer.write(b"\n"); } } Err(_) => { @@ -51,7 +51,7 @@ fn persist_m3u_playlist_as_text(target: &ConfigTarget, cfg: &Config, m3u_playlis } } -pub(crate) fn m3u_write_playlist(target: &ConfigTarget, cfg: &Config, target_path: &Path, new_playlist: &[PlaylistGroup]) -> Result<(), M3uFilterError> { +pub fn m3u_write_playlist(target: &ConfigTarget, cfg: &Config, target_path: &Path, new_playlist: &[PlaylistGroup]) -> Result<(), M3uFilterError> { if !new_playlist.is_empty() { let (m3u_path, idx_path) = m3u_get_file_paths(target_path); let m3u_playlist = new_playlist.iter() @@ -79,7 +79,7 @@ pub(crate) fn m3u_write_playlist(target: &ConfigTarget, cfg: &Config, target_pat Ok(()) } -pub(crate) fn m3u_load_rewrite_playlist( +pub fn m3u_load_rewrite_playlist( cfg: &Config, target: &ConfigTarget, user: &ProxyUserCredentials, @@ -88,7 +88,7 @@ pub(crate) fn m3u_load_rewrite_playlist( } -pub(crate) fn m3u_get_item_for_stream_id(cfg: &Config, stream_id: u32, m3u_path: &Path, idx_path: &Path) -> Result { +pub fn m3u_get_item_for_stream_id(cfg: &Config, stream_id: u32, m3u_path: &Path, idx_path: &Path) -> Result { if stream_id < 1 { return Err(Error::new(ErrorKind::Other, "id should start with 1")); } diff --git a/src/repository/mod.rs b/src/repository/mod.rs index 23b81344b..e2d3e6496 100644 --- a/src/repository/mod.rs +++ b/src/repository/mod.rs @@ -1,12 +1,12 @@ -pub(crate) mod playlist_repository; -pub(crate) mod m3u_repository; -pub(crate) mod xtream_repository; -pub(crate) mod epg_repository; -pub(crate) mod kodi_repository; -pub(crate) mod storage; +pub mod playlist_repository; +pub mod m3u_repository; +pub mod xtream_repository; +pub mod epg_repository; +pub mod kodi_repository; +pub mod storage; mod indexed_document; -pub(crate) mod target_id_mapping; -pub(crate) mod bplustree; -pub(crate) mod m3u_playlist_iterator; -pub(crate) mod xtream_playlist_iterator; +pub mod target_id_mapping; +pub mod bplustree; +pub mod m3u_playlist_iterator; +pub mod xtream_playlist_iterator; diff --git a/src/repository/playlist_repository.rs b/src/repository/playlist_repository.rs index 0479ab163..f545b6e42 100644 --- a/src/repository/playlist_repository.rs +++ b/src/repository/playlist_repository.rs @@ -9,7 +9,7 @@ use crate::repository::storage::{ensure_target_storage_path, get_target_id_mappi use crate::repository::target_id_mapping::TargetIdMapping; use crate::repository::xtream_repository::xtream_write_playlist; -pub(crate) fn persist_playlist(playlist: &mut [PlaylistGroup], epg: Option<&Epg>, +pub fn persist_playlist(playlist: &mut [PlaylistGroup], epg: Option<&Epg>, target: &ConfigTarget, cfg: &Config) -> Result<(), Vec> { let mut errors = vec![]; let target_path = match ensure_target_storage_path(cfg, &target.name) { diff --git a/src/repository/storage.rs b/src/repository/storage.rs index 9ec25703d..676ec7f62 100644 --- a/src/repository/storage.rs +++ b/src/repository/storage.rs @@ -9,7 +9,7 @@ pub(in crate::repository) const FILE_SUFFIX_INDEX: &str = "idx"; const FILE_ID_MAPPING: &str = "id_mapping.db"; -pub(crate) fn hash_string(url: &str) -> [u8; 32] { +pub fn hash_string(url: &str) -> [u8; 32] { let hash = blake3::hash(url.as_bytes()); hash.into() // convert to hash array } @@ -21,7 +21,7 @@ fn hex_encode(bytes: &[u8]) -> String { }) } -pub(crate) fn hash_string_as_hex(url: &str) -> String { +pub fn hash_string_as_hex(url: &str) -> String { hex_encode(&hash_string(url)) } @@ -29,7 +29,7 @@ pub(in crate::repository) fn get_target_id_mapping_file(target_path: &Path) -> P target_path.join(PathBuf::from(FILE_ID_MAPPING)) } -pub(crate) fn ensure_target_storage_path(cfg: &Config, target_name: &str) -> Result { +pub fn ensure_target_storage_path(cfg: &Config, target_name: &str) -> Result { if let Some(path) = get_target_storage_path(cfg, target_name) { if std::fs::create_dir_all(&path).is_err() { let msg = format!("Failed to save target data, can't create directory {}", &path.to_str().unwrap()); @@ -42,11 +42,11 @@ pub(crate) fn ensure_target_storage_path(cfg: &Config, target_name: &str) -> Res } } -pub(crate) fn get_target_storage_path(cfg: &Config, target_name: &str) -> Option { +pub fn get_target_storage_path(cfg: &Config, target_name: &str) -> Option { file_utils::get_file_path(&cfg.working_dir, Some(std::path::PathBuf::from(target_name.replace(' ', "_")))) } -pub(crate) fn get_input_storage_path(input: &ConfigInput, working_dir: &str) -> std::io::Result { +pub fn get_input_storage_path(input: &ConfigInput, working_dir: &str) -> std::io::Result { let name = format!("input_{}", input.name.clone().unwrap_or_else(|| format!("{}", input.id))); let path = Path::new(working_dir).join(name); diff --git a/src/repository/target_id_mapping.rs b/src/repository/target_id_mapping.rs index 819300fa7..f72da1b5c 100644 --- a/src/repository/target_id_mapping.rs +++ b/src/repository/target_id_mapping.rs @@ -14,7 +14,7 @@ use crate::repository::bplustree::BPlusTree; const EXPIRATION_DURATION: i64 = 86400; #[derive(Serialize, Deserialize, Clone)] -pub(crate) struct VirtualIdRecord { +pub struct VirtualIdRecord { pub virtual_id: u32, pub provider_id: u32, pub uuid: [u8; 32], @@ -26,19 +26,19 @@ pub(crate) struct VirtualIdRecord { impl VirtualIdRecord { fn new(provider_id: u32, virtual_id: u32, item_type: PlaylistItemType, parent_virtual_id: u32, uuid: [u8; 32]) -> Self { let last_updated = Local::now().timestamp(); - VirtualIdRecord { virtual_id, provider_id, uuid, item_type, parent_virtual_id, last_updated } + Self { virtual_id, provider_id, uuid, item_type, parent_virtual_id, last_updated } } - pub(crate) fn is_expired(&self) -> bool { + pub fn is_expired(&self) -> bool { (Local::now().timestamp() - self.last_updated) > EXPIRATION_DURATION } - pub(crate) fn copy_update_timestamp(&self) -> Self { + pub fn copy_update_timestamp(&self) -> Self { Self::new(self.provider_id, self.virtual_id, self.item_type, self.parent_virtual_id, self.uuid) } } -pub(crate) struct TargetIdMapping { +pub struct TargetIdMapping { dirty: bool, virtual_id_counter: u32, by_virtual_id: BPlusTree, @@ -47,11 +47,8 @@ pub(crate) struct TargetIdMapping { } impl TargetIdMapping { - pub(crate) fn new(path: &Path) -> Self { - let tree_virtual_id: BPlusTree = match BPlusTree::::load(path) { - Ok(tree) => tree, - _ => BPlusTree::::new() - }; + pub fn new(path: &Path) -> Self { + let tree_virtual_id: BPlusTree = BPlusTree::::load(path).unwrap_or_else(|_| BPlusTree::::new()); let mut tree_uuid = BTreeMap::new(); let mut virtual_id_counter: u32 = 0; tree_virtual_id.traverse(|keys, values| { @@ -65,7 +62,7 @@ impl TargetIdMapping { tree_uuid.insert(v.uuid, v.virtual_id); } }); - TargetIdMapping { + Self { dirty: false, virtual_id_counter, by_virtual_id: tree_virtual_id, @@ -74,7 +71,7 @@ impl TargetIdMapping { } } - pub(crate) fn insert_entry(&mut self, uuid: [u8; 32], provider_id: u32, item_type: PlaylistItemType, parent_virtual_id: u32) -> u32 { + pub fn insert_entry(&mut self, uuid: [u8; 32], provider_id: u32, item_type: PlaylistItemType, parent_virtual_id: u32) -> u32 { match self.by_uuid.get(&uuid) { None => { self.dirty = true; @@ -87,7 +84,7 @@ impl TargetIdMapping { } } - pub(crate) fn persist(&mut self) -> Result<(), Error> { + pub fn persist(&mut self) -> Result<(), Error> { if self.dirty { self.by_virtual_id.store(&self.path)?; } diff --git a/src/repository/xtream_playlist_iterator.rs b/src/repository/xtream_playlist_iterator.rs index b1eef5545..67314b2e9 100644 --- a/src/repository/xtream_playlist_iterator.rs +++ b/src/repository/xtream_playlist_iterator.rs @@ -7,7 +7,7 @@ use crate::repository::indexed_document::IndexedDocumentReader; use crate::repository::xtream_repository::{xtream_get_file_paths, xtream_get_storage_path}; use crate::utils::file_lock_manager::FileReadGuard; -pub(crate) struct XtreamPlaylistIterator { +pub struct XtreamPlaylistIterator { reader: IndexedDocumentReader, options: XtreamMappingOptions, category_id: u32, diff --git a/src/repository/xtream_repository.rs b/src/repository/xtream_repository.rs index 956c4c2aa..ec07d9e0e 100644 --- a/src/repository/xtream_repository.rs +++ b/src/repository/xtream_repository.rs @@ -18,12 +18,12 @@ use crate::repository::target_id_mapping::{TargetIdMapping, VirtualIdRecord}; use crate::repository::xtream_playlist_iterator::XtreamPlaylistIterator; use crate::utils::json_utils::{json_iter_array, json_write_documents_to_file}; -pub(crate) static COL_CAT_LIVE: &str = "cat_live"; -pub(crate) static COL_CAT_SERIES: &str = "cat_series"; -pub(crate) static COL_CAT_VOD: &str = "cat_vod"; +pub static COL_CAT_LIVE: &str = "cat_live"; +pub static COL_CAT_SERIES: &str = "cat_series"; +pub static COL_CAT_VOD: &str = "cat_vod"; const FILE_SERIES_EPISODES: &str = "series_episodes"; const FILE_SERIES: &str = "series"; -pub(crate) const FILE_EPG: &str = "epg.xml"; +pub const FILE_EPG: &str = "epg.xml"; const PATH_XTREAM: &str = "xtream"; const TAG_CATEGORY_ID: &str = "category_id"; const TAG_CATEGORY_NAME: &str = "category_name"; @@ -135,11 +135,11 @@ fn load_old_category_ids(path: &Path) -> (u32, HashMap) { (max_id, result) } -pub(crate) fn xtream_get_storage_path(cfg: &Config, target_name: &str) -> Option { +pub fn xtream_get_storage_path(cfg: &Config, target_name: &str) -> Option { get_target_storage_path(cfg, target_name).map(|target_path| target_path.join(PathBuf::from(PATH_XTREAM))) } -pub(crate) fn xtream_get_epg_file_path(path: &Path) -> PathBuf { +pub fn xtream_get_epg_file_path(path: &Path) -> PathBuf { path.join(FILE_EPG) } @@ -149,11 +149,11 @@ fn xtream_get_file_paths_for_name(storage_path: &Path, name: &str) -> (PathBuf, (xtream_path, index_path) } -pub(crate) fn xtream_get_file_paths(storage_path: &Path, cluster: XtreamCluster) -> (PathBuf, PathBuf) { +pub fn xtream_get_file_paths(storage_path: &Path, cluster: XtreamCluster) -> (PathBuf, PathBuf) { xtream_get_file_paths_for_name(storage_path, &cluster.as_str().to_lowercase()) } -pub(crate) fn xtream_get_file_paths_for_series(storage_path: &Path) -> (PathBuf, PathBuf) { +pub fn xtream_get_file_paths_for_series(storage_path: &Path) -> (PathBuf, PathBuf) { xtream_get_file_paths_for_name(storage_path, FILE_SERIES) } @@ -168,7 +168,7 @@ fn xtream_garbage_collect(config: &Config, target_name: &str) -> std::io::Result Ok(()) } -pub(crate) fn xtream_write_playlist(target: &ConfigTarget, cfg: &Config, playlist: &mut [PlaylistGroup]) -> Result<(), M3uFilterError> { +pub fn xtream_write_playlist(target: &ConfigTarget, cfg: &Config, playlist: &mut [PlaylistGroup]) -> Result<(), M3uFilterError> { let path = ensure_xtream_storage_path(cfg, target.name.as_str())?; let mut errors = Vec::new(); let mut cat_live_col = vec![]; @@ -259,7 +259,7 @@ pub(crate) fn xtream_write_playlist(target: &ConfigTarget, cfg: &Config, playlis Ok(()) } -pub(crate) fn xtream_get_collection_path(cfg: &Config, target_name: &str, collection_name: &str) -> Result<(Option, Option), Error> { +pub fn xtream_get_collection_path(cfg: &Config, target_name: &str, collection_name: &str) -> Result<(Option, Option), Error> { if let Some(path) = xtream_get_storage_path(cfg, target_name) { let col_path = get_collection_path(&path, collection_name); if col_path.exists() { @@ -292,7 +292,7 @@ macro_rules! try_cluster { }; } -pub(crate) fn xtream_get_item_for_stream_id( +pub fn xtream_get_item_for_stream_id( virtual_id: u32, config: &Config, target: &ConfigTarget, @@ -336,11 +336,11 @@ pub(crate) fn xtream_get_item_for_stream_id( } -pub(crate) fn xtream_load_rewrite_playlist(cluster: XtreamCluster, config: &Config, target: &ConfigTarget, category_id: u32) -> Result>, M3uFilterError> { +pub fn xtream_load_rewrite_playlist(cluster: XtreamCluster, config: &Config, target: &ConfigTarget, category_id: u32) -> Result>, M3uFilterError> { Ok(Box::new(XtreamPlaylistIterator::new(cluster, config, target, category_id)?)) } -pub(crate) fn xtream_write_series_info(config: &Config, target_name: &str, +pub fn xtream_write_series_info(config: &Config, target_name: &str, series_info_id: u32, content: &str) -> Result<(), Error> { let target_path = try_option_ok!(get_target_storage_path(config, target_name)); @@ -349,7 +349,7 @@ pub(crate) fn xtream_write_series_info(config: &Config, target_name: &str, { let _file_lock = config.file_locks.write_lock(&info_path)?; - let mut writer = IndexedDocumentWriter::new_append(info_path.clone(), idx_path)?; + let mut writer = IndexedDocumentWriter::new_append(info_path, idx_path)?; writer .write_doc(series_info_id, content) .map_err(|_| Error::new(ErrorKind::Other, format!("failed to write xtream series info for target {target_name}")))?; @@ -371,7 +371,7 @@ pub(crate) fn xtream_write_series_info(config: &Config, target_name: &str, } // Reads the series info entry if exists, otherwise error -pub(crate) fn xtream_load_series_info(config: &Config, target_name: &str, series_id: u32) -> Option { +pub fn xtream_load_series_info(config: &Config, target_name: &str, series_id: u32) -> Option { let target_path = get_target_storage_path(config, target_name)?; let storage_path = xtream_get_storage_path(config, target_name)?; @@ -414,7 +414,7 @@ pub(crate) fn xtream_load_series_info(config: &Config, target_name: &str, series None } -pub(crate) fn write_and_get_xtream_series_info( +pub fn write_and_get_xtream_series_info( config: &Config, target: &ConfigTarget, pli_series_info: &XtreamPlaylistItem, diff --git a/src/utils/compressed_file_reader.rs b/src/utils/compressed_file_reader.rs index 77659b410..9437bfd6d 100644 --- a/src/utils/compressed_file_reader.rs +++ b/src/utils/compressed_file_reader.rs @@ -5,12 +5,12 @@ use flate2::bufread::{GzDecoder, ZlibDecoder}; use crate::utils::compression_utils::{is_deflate, is_gzip}; -pub(crate) struct CompressedFileReader { +pub struct CompressedFileReader { reader: BufReader>, } impl CompressedFileReader { - pub(crate) fn new(path: &Path) -> std::io::Result { + pub fn new(path: &Path) -> std::io::Result { let file = OpenOptions::new() .read(true) .open(path)?; diff --git a/src/utils/compression_utils.rs b/src/utils/compression_utils.rs index a01cd6200..c5cb7845c 100644 --- a/src/utils/compression_utils.rs +++ b/src/utils/compression_utils.rs @@ -1,12 +1,12 @@ -pub(crate) const ENCODING_GZIP: &str = "gzip"; -pub(crate) const ENCODING_DEFLATE: &str = "deflate"; +pub const ENCODING_GZIP: &str = "gzip"; +pub const ENCODING_DEFLATE: &str = "deflate"; -pub(crate) fn is_gzip(bytes: &[u8]) -> bool { +pub const fn is_gzip(bytes: &[u8]) -> bool { // Gzip files start with the bytes 0x1F 0x8B bytes.len() >= 2 && bytes[0] == 0x1F && bytes[1] == 0x8B } -pub(crate) fn is_deflate(bytes: &[u8]) -> bool { +pub const fn is_deflate(bytes: &[u8]) -> bool { bytes[0] == 0x78 && (bytes[1] == 0x01 || bytes[1] == 0x9C || bytes[1] == 0xDA) } \ No newline at end of file diff --git a/src/utils/config_reader.rs b/src/utils/config_reader.rs index f1e27e696..9b9deef09 100644 --- a/src/utils/config_reader.rs +++ b/src/utils/config_reader.rs @@ -14,8 +14,8 @@ use crate::model::config::{Config, ConfigDto}; use crate::model::mapping::Mappings; use crate::utils::{file_utils, multi_file_reader}; -pub(crate) fn read_mappings(args_mapping: Option, cfg: &mut Config) -> Result<(), M3uFilterError> { - let mappings_file: String = args_mapping.unwrap_or(file_utils::get_default_mappings_path(cfg.t_config_path.as_str())); +pub fn read_mappings(args_mapping: Option, cfg: &mut Config) -> Result<(), M3uFilterError> { + let mappings_file: String = args_mapping.unwrap_or_else(|| file_utils::get_default_mappings_path(cfg.t_config_path.as_str())); match read_mapping(mappings_file.as_str()) { Ok(mappings) => { @@ -29,8 +29,8 @@ pub(crate) fn read_mappings(args_mapping: Option, cfg: &mut Config) -> R } } -pub(crate) fn read_api_proxy_config(args_api_proxy_config: Option, cfg: &mut Config) { - let api_proxy_config_file: String = args_api_proxy_config.unwrap_or(file_utils::get_default_api_proxy_config_path(cfg.t_config_path.as_str())); +pub fn read_api_proxy_config(args_api_proxy_config: Option, cfg: &mut Config) { + let api_proxy_config_file: String = args_api_proxy_config.unwrap_or_else(|| file_utils::get_default_api_proxy_config_path(cfg.t_config_path.as_str())); api_proxy_config_file.clone_into(&mut cfg.t_api_proxy_file_path); let api_proxy_config = read_api_proxy(api_proxy_config_file.as_str(), true); match api_proxy_config { @@ -44,7 +44,7 @@ pub(crate) fn read_api_proxy_config(args_api_proxy_config: Option, cfg: } } -pub(crate) fn read_config(config_path: &str, config_file: &str, sources_file: &str) -> Result { +pub fn read_config(config_path: &str, config_file: &str, sources_file: &str) -> Result { let files = vec![std::path::PathBuf::from(config_file), std::path::PathBuf::from(sources_file)]; match multi_file_reader::MultiFileReader::new(&files) { Ok(file) => { @@ -67,7 +67,7 @@ pub(crate) fn read_config(config_path: &str, config_file: &str, sources_file: &s } } -pub(crate) fn read_mapping(mapping_file: &str) -> Result, M3uFilterError> { +pub fn read_mapping(mapping_file: &str) -> Result, M3uFilterError> { let mapping_file = std::path::PathBuf::from(mapping_file); if let Ok(file) = file_utils::open_file(&mapping_file) { info!("Mapping file: {}", mapping_file.to_str().unwrap_or("?")); @@ -86,9 +86,8 @@ pub(crate) fn read_mapping(mapping_file: &str) -> Result, M3uFi Ok(None) } -pub(crate) fn read_api_proxy(api_proxy_file: &str, resolve_var: bool) -> Option { - match file_utils::open_file(&std::path::PathBuf::from(api_proxy_file)) { - Ok(file) => { +pub fn read_api_proxy(api_proxy_file: &str, resolve_var: bool) -> Option { + file_utils::open_file(&std::path::PathBuf::from(api_proxy_file)).map_or(None, |file| { let mapping: Result = serde_yaml::from_reader(file); match mapping { Ok(mut result) => { @@ -107,9 +106,7 @@ pub(crate) fn read_api_proxy(api_proxy_file: &str, resolve_var: bool) -> Option< None } } - } - _ => None - } + }) } fn write_config_file(file_path: &str, backup_dir: &str, config: &T, default_name: &str) -> Result<(), M3uFilterError> @@ -134,24 +131,21 @@ fn write_config_file(file_path: &str, backup_dir: &str, config: &T, default_n } } -pub(crate) fn save_api_proxy(file_path: &str, backup_dir: &str, config: &ApiProxyConfig) -> Result<(), M3uFilterError> { +pub fn save_api_proxy(file_path: &str, backup_dir: &str, config: &ApiProxyConfig) -> Result<(), M3uFilterError> { write_config_file(file_path, backup_dir, config, "api-proxy.yml") } -pub(crate) fn save_main_config(file_path: &str, backup_dir: &str, config: &ConfigDto) -> Result<(), M3uFilterError> { +pub fn save_main_config(file_path: &str, backup_dir: &str, config: &ConfigDto) -> Result<(), M3uFilterError> { write_config_file(file_path, backup_dir, config, "config.yml") } -pub(crate) fn resolve_env_var(value: &str) -> String { +pub fn resolve_env_var(value: &str) -> String { if !value.trim().is_empty() { let pattern = Regex::new(r"\$\{env:(?P[a-zA-Z_][a-zA-Z0-9_]*)}").unwrap(); if let Some(caps) = pattern.captures(value) { if let Some(var) = caps.name("var") { let var_name = var.as_str(); - return match env::var(var_name) { - Ok(resolved_val) => resolved_val, // If environment variable found, replace with its value - Err(_) => value.to_string() - }; + return env::var(var_name).unwrap_or_else(|_| value.to_string()); } } } diff --git a/src/utils/default_utils.rs b/src/utils/default_utils.rs index 946543be7..5ba750521 100644 --- a/src/utils/default_utils.rs +++ b/src/utils/default_utils.rs @@ -1,5 +1,5 @@ -pub(crate) fn default_as_true() -> bool { true } +pub const fn default_as_true() -> bool { true } -pub(crate) fn default_as_default() -> String { String::from("default") } +pub fn default_as_default() -> String { String::from("default") } -pub(crate) fn default_as_two_u16() -> u16 { 2 } +pub const fn default_as_two_u16() -> u16 { 2 } diff --git a/src/utils/directed_graph.rs b/src/utils/directed_graph.rs index 0239e5808..0e982f9e2 100644 --- a/src/utils/directed_graph.rs +++ b/src/utils/directed_graph.rs @@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet}; use std::fmt::{Debug, Display, Formatter, Result}; #[derive(Debug)] -pub(crate) struct DirectedGraph +pub struct DirectedGraph where K: Eq + std::hash::Hash + Clone + Display + Debug, { @@ -13,19 +13,19 @@ impl DirectedGraph where K: Eq + std::hash::Hash + Clone + Display + Debug, { - pub(crate) fn new() -> Self { - DirectedGraph { + pub fn new() -> Self { + Self { adjacencies: HashMap::new(), } } // Add a node to the graph, ignore if it already exists - pub(crate) fn add_node(&mut self, node: &K) { + pub fn add_node(&mut self, node: &K) { self.adjacencies.entry(node.to_owned()).or_default(); } // Add a directed edge to the graph, ignore if it already exists - pub(crate) fn add_edge(&mut self, from: &K, to: &K) { + pub fn add_edge(&mut self, from: &K, to: &K) { match self.adjacencies.entry(from.clone()) { std::collections::hash_map::Entry::Occupied(mut entry) => { let edges = entry.get_mut(); @@ -40,7 +40,7 @@ where } // Detect and return cycles in the graph - pub(crate) fn find_cycles(&self) -> Vec> { + pub fn find_cycles(&self) -> Vec> { let mut visited = HashSet::new(); let mut recursion_stack = Vec::new(); let mut cycles = Vec::new(); @@ -81,7 +81,7 @@ where recursion_stack.pop(); } - pub(crate) fn has_cycle(&self) -> bool { + pub fn has_cycle(&self) -> bool { let mut visited = HashSet::new(); let mut recursion_stack = HashSet::new(); @@ -118,7 +118,7 @@ where } // Return all dependencies as a NodeDependencies struct if no cyclic dependencies exist - pub(crate) fn get_dependencies(&self) -> Option>> { + pub fn get_dependencies(&self) -> Option>> { if self.has_cycle() { return None; } @@ -137,7 +137,7 @@ where // Topological sort function - pub(crate) fn topological_sort(&self) -> Option> { + pub fn topological_sort(&self) -> Option> { let mut visited = HashSet::new(); let mut temp_mark = HashSet::new(); let mut result = Vec::new(); diff --git a/src/utils/download.rs b/src/utils/download.rs index 26a4dc834..907eb7a77 100644 --- a/src/utils/download.rs +++ b/src/utils/download.rs @@ -15,17 +15,14 @@ fn prepare_file_path(persist: Option<&String>, working_dir: &str, action: &str) persist.map(|persist_path| file_utils::prepare_persist_path(persist_path.as_str(), action)); if persist_file.is_some() { let file_path = file_utils::get_file_path(working_dir, persist_file); - debug!("persist to file: {:?}", match &file_path { - Some(fp) => fp.display().to_string(), - _ => String::new() - }); + debug!("persist to file: {file_path:?}"); file_path } else { None } } -pub(crate) async fn get_m3u_playlist(cfg: &Config, input: &ConfigInput, working_dir: &String) -> (Vec, Vec) { +pub async fn get_m3u_playlist(cfg: &Config, input: &ConfigInput, working_dir: &String) -> (Vec, Vec) { let url = input.url.clone(); let persist_file_path = prepare_file_path(input.persist.as_ref(), working_dir, ""); match request_utils::get_input_text_content(input, working_dir, &url, persist_file_path).await { @@ -36,7 +33,7 @@ pub(crate) async fn get_m3u_playlist(cfg: &Config, input: &ConfigInput, working_ } } -pub(crate) async fn get_xtream_playlist_series<'a>(fpl: &mut FetchedPlaylist<'a>, errors: &mut Vec, resolve_delay: u16) -> Vec { +pub async fn get_xtream_playlist_series<'a>(fpl: &mut FetchedPlaylist<'a>, errors: &mut Vec, resolve_delay: u16) -> Vec { let input = fpl.input; let mut result: Vec = vec![]; for plg in &mut fpl.playlistgroups { @@ -106,7 +103,7 @@ const ACTIONS: [(XtreamCluster, &str, &str); 3] = [ (XtreamCluster::Video, "get_vod_categories", "get_vod_streams"), (XtreamCluster::Series, "get_series_categories", "get_series")]; -pub(crate) async fn get_xtream_playlist(input: &ConfigInput, working_dir: &str) -> (Vec, Vec) { +pub async fn get_xtream_playlist(input: &ConfigInput, working_dir: &str) -> (Vec, Vec) { let mut playlist_groups: Vec = Vec::new(); let username = input.username.as_ref().map_or("", |v| v); let password = input.password.as_ref().map_or("", |v| v); @@ -154,7 +151,7 @@ pub(crate) async fn get_xtream_playlist(input: &ConfigInput, working_dir: &str) (playlist_groups, errors) } -pub(crate) async fn get_xmltv(_cfg: &Config, input: &ConfigInput, working_dir: &str) -> (Option, Vec) { +pub async fn get_xmltv(_cfg: &Config, input: &ConfigInput, working_dir: &str) -> (Option, Vec) { match &input.epg_url { None => (None, vec![]), Some(url) => { diff --git a/src/utils/file_lock_manager.rs b/src/utils/file_lock_manager.rs index 6e16675e3..273234598 100644 --- a/src/utils/file_lock_manager.rs +++ b/src/utils/file_lock_manager.rs @@ -4,19 +4,19 @@ use std::{fmt, io}; use std::path::{Path, PathBuf}; #[derive(Clone)] -pub(crate) struct FileLockManager { +pub struct FileLockManager { locks: Arc>>>>, } impl FileLockManager { - pub(crate) fn new() -> Self { + pub fn new() -> Self { Self { locks: Arc::new(Mutex::new(HashMap::new())), } } // Acquires a read lock for the specified file and returns a FileReadGuard. - pub(crate) fn read_lock(&self, path: &Path) -> io::Result { + pub fn read_lock(&self, path: &Path) -> io::Result { let file_lock = self.get_or_create_lock(path)?; let guard = file_lock.read().map_err(|_| { io::Error::new(io::ErrorKind::Other, "Failed to acquire read lock") @@ -26,7 +26,7 @@ impl FileLockManager { } // Acquires a write lock for the specified file and returns a FileWriteGuard. - pub(crate) fn write_lock(&self, path: &Path) -> io::Result { + pub fn write_lock(&self, path: &Path) -> io::Result { let file_lock = self.get_or_create_lock(path)?; let guard = file_lock.write().map_err(|_| { io::Error::new(io::ErrorKind::Other, "Failed to acquire write lock") @@ -47,13 +47,14 @@ impl FileLockManager { let file_lock = Arc::new(RwLock::new(())); locks.insert(path.to_path_buf(), file_lock.clone()); + drop(locks); Ok(file_lock) } } impl Default for FileLockManager { fn default() -> Self { - FileLockManager::new() + Self::new() } } @@ -61,7 +62,6 @@ impl fmt::Debug for FileLockManager { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Acquire the lock to safely access the HashMap let locks = self.locks.lock().unwrap(); - // Format the paths in the HashMap for debug purposes let keys: Vec<_> = locks.keys().collect(); f.debug_struct("FileLockManager") .field("locks", &keys) @@ -71,7 +71,7 @@ impl fmt::Debug for FileLockManager { // Define FileReadGuard to hold both the lock reference and the actual read guard. #[allow(dead_code)] -pub(crate) struct FileReadGuard { +pub struct FileReadGuard { lock: Arc>, guard: RwLockReadGuard<'static, ()>, } @@ -80,7 +80,7 @@ impl FileReadGuard { pub fn new(lock: Arc>, guard: RwLockReadGuard<'_, ()>) -> Self { // Convert the lifetime of `guard` to 'static by transmuting. let static_guard: RwLockReadGuard<'static, ()> = unsafe { std::mem::transmute(guard) }; - FileReadGuard { + Self { lock, guard: static_guard, } @@ -89,7 +89,7 @@ impl FileReadGuard { // Define FileWriteGuard to hold both the lock reference and the actual write guard. #[allow(dead_code)] -pub(crate) struct FileWriteGuard { +pub struct FileWriteGuard { lock: Arc>, guard: RwLockWriteGuard<'static, ()>, } @@ -98,7 +98,7 @@ impl FileWriteGuard { pub fn new(lock: Arc>, guard: RwLockWriteGuard<'_, ()>) -> Self { // Convert the lifetime of `guard` to 'static by transmuting. let static_guard: RwLockWriteGuard<'static, ()> = unsafe { std::mem::transmute(guard) }; - FileWriteGuard { + Self { lock, guard: static_guard, } diff --git a/src/utils/file_reader.rs b/src/utils/file_reader.rs index 5d761c9f6..f45861b8e 100644 --- a/src/utils/file_reader.rs +++ b/src/utils/file_reader.rs @@ -1,12 +1,12 @@ use std::fs::File; use linereader::LineReader; -pub(crate) struct FileReader { +pub struct FileReader { reader: LineReader, } impl FileReader { - pub(crate) fn new(file: File) -> Self { + pub fn new(file: File) -> Self { Self { reader: LineReader::new(file), } diff --git a/src/utils/file_utils.rs b/src/utils/file_utils.rs index ba8ba82ae..9e5e522b8 100644 --- a/src/utils/file_utils.rs +++ b/src/utils/file_utils.rs @@ -1,5 +1,5 @@ use std::fs; -use std::fs::{File}; +use std::fs::File; use std::io::Write; use std::path::{Path, PathBuf}; @@ -21,7 +21,7 @@ macro_rules! exit { }}; } -pub(crate) fn get_exe_path() -> PathBuf { +pub fn get_exe_path() -> PathBuf { let default_path = std::path::PathBuf::from("./"); let current_exe = std::env::current_exe(); match current_exe { @@ -45,7 +45,7 @@ fn get_default_path(file: &str) -> String { }) } -pub(crate) fn get_default_file_path(config_path: &str, file: &str) -> String { +pub fn get_default_file_path(config_path: &str, file: &str) -> String { let path: PathBuf = PathBuf::from(config_path); let default_path = path.join(file); String::from(if default_path.exists() { @@ -56,75 +56,62 @@ pub(crate) fn get_default_file_path(config_path: &str, file: &str) -> String { } #[inline] -pub(crate) fn get_default_user_file_path(config_path: &str) -> String { +pub fn get_default_user_file_path(config_path: &str) -> String { get_default_file_path(config_path, USER_FILE) } #[inline] -pub(crate) fn get_default_config_path() -> String { +pub fn get_default_config_path() -> String { get_default_path(CONFIG_PATH) } #[inline] -pub(crate) fn get_default_config_file_path(config_path: &str) -> String { +pub fn get_default_config_file_path(config_path: &str) -> String { get_default_file_path(config_path, CONFIG_FILE) } #[inline] -pub(crate) fn get_default_sources_file_path(config_path: &str) -> String { +pub fn get_default_sources_file_path(config_path: &str) -> String { get_default_file_path(config_path, SOURCE_FILE) } #[inline] -pub(crate) fn get_default_mappings_path(config_path: &str) -> String { +pub fn get_default_mappings_path(config_path: &str) -> String { get_default_file_path(config_path, MAPPING_FILE) } #[inline] -pub(crate) fn get_default_api_proxy_config_path(config_path: &str) -> String { +pub fn get_default_api_proxy_config_path(config_path: &str) -> String { get_default_file_path(config_path, API_PROXY_FILE) } -pub(crate) fn get_working_path(wd: &String) -> String { +pub fn get_working_path(wd: &String) -> String { let current_dir = std::env::current_dir().unwrap(); if wd.is_empty() { String::from(current_dir.to_str().unwrap_or(".")) } else { let work_path = std::path::PathBuf::from(wd); let _ = fs::create_dir_all(&work_path); - let wdpath = match fs::metadata(&work_path) { - Ok(md) => { - if md.is_dir() && !md.permissions().readonly() { - match work_path.canonicalize() { - Ok(ap) => Some(ap), - Err(_) => None - } - } else { - error!("Path not found {:?}", &work_path); - None - } - } - Err(_) => None, - }; - let rp: PathBuf = match wdpath { - Some(d) => d, - None => current_dir.join(wd) - }; - if let Ok(ap) = rp.canonicalize() { - String::from(ap.to_str().unwrap_or("./")) + let wdpath = fs::metadata(&work_path).map_or(None, |md| if md.is_dir() && !md.permissions().readonly() { + work_path.canonicalize().ok() } else { + error!("Path not found {:?}", &work_path); + None + }); + let rp: PathBuf = wdpath.map_or_else(|| current_dir.join(wd), |d| d); + rp.canonicalize().map_or_else(|_| { error!("Path not found {:?}", &rp); String::from("./") - } + }, |ap| String::from(ap.to_str().unwrap_or("./"))) } } #[inline] -pub(crate) fn open_file(file_name: &Path) -> Result { +pub fn open_file(file_name: &Path) -> Result { File::open(file_name) } -pub(crate) fn persist_file(persist_file: Option, text: &String) { +pub fn persist_file(persist_file: Option, text: &String) { if let Some(path_buf) = persist_file { let filename = &path_buf.to_str().unwrap_or("?"); match File::create(&path_buf) { @@ -137,33 +124,28 @@ pub(crate) fn persist_file(persist_file: Option, text: &String) { } } -pub(crate) fn prepare_persist_path(file_name: &str, date_prefix: &str) -> PathBuf { +pub fn prepare_persist_path(file_name: &str, date_prefix: &str) -> PathBuf { let now = chrono::Local::now(); let persist_filename = file_name.replace("{}", format!("{date_prefix}{}", now.format("%Y%m%d_%H%M%S").to_string().as_str()).as_str()); std::path::PathBuf::from(persist_filename) } -pub(crate) fn get_file_path(wd: &str, path: Option) -> Option { - match path { - Some(p) => { - if p.is_relative() { +pub fn get_file_path(wd: &str, path: Option) -> Option { + path.map(|p| if p.is_relative() { let pb = PathBuf::from(wd); match pb.join(&p).absolutize() { - Ok(os) => Some(PathBuf::from(os)), + Ok(os) => PathBuf::from(os), Err(e) => { error!("path is not relative {:?}", e); - Some(p) + p } } } else { - Some(p) - } - } - None => None - } + p + }) } -pub(crate) fn add_prefix_to_filename(path: &Path, prefix: &str, ext: Option<&str>) -> PathBuf { +pub fn add_prefix_to_filename(path: &Path, prefix: &str, ext: Option<&str>) -> PathBuf { let file_name = path.file_name().unwrap_or_default(); let new_file_name = format!("{}{}", prefix, file_name.to_string_lossy()); let result = path.with_file_name(new_file_name); @@ -173,28 +155,28 @@ pub(crate) fn add_prefix_to_filename(path: &Path, prefix: &str, ext: Option<&str } } -pub(crate) fn path_exists(file_path: &Path) -> bool { +pub fn path_exists(file_path: &Path) -> bool { if let Ok(metadata) = fs::metadata(file_path) { return metadata.is_file(); } false } -pub(crate) fn check_write(res: &std::io::Result<()>) -> Result<(), std::io::Error> { +pub fn check_write(res: &std::io::Result<()>) -> Result<(), std::io::Error> { match res { Ok(()) => Ok(()), Err(_) => Err(std::io::Error::new(std::io::ErrorKind::Other, "Unable to write file")), } } -pub(crate) fn append_extension(path: &Path, ext: &str) -> PathBuf { +pub fn append_extension(path: &Path, ext: &str) -> PathBuf { let extension = path.extension().map(|ext| ext.to_str().unwrap_or("")); path.with_extension(format!("{}{ext}", &extension.unwrap_or_default())) } #[inline] -pub(crate) fn sanitize_filename(file_name: &str) -> String { +pub fn sanitize_filename(file_name: &str) -> String { file_name .chars() .map(|c| if c.is_alphanumeric() || c == '_' || c == '-' { c } else { '_' }) diff --git a/src/utils/json_utils.rs b/src/utils/json_utils.rs index daa73e6a0..abb9af433 100644 --- a/src/utils/json_utils.rs +++ b/src/utils/json_utils.rs @@ -23,10 +23,7 @@ fn invalid_data(msg: &str) -> io::Error { fn deserialize_single(reader: R) -> io::Result { let next_obj = Deserializer::from_reader(reader).into_iter::().next(); - match next_obj { - Some(result) => result.map_err(Into::into), - None => Err(invalid_data("premature EOF")), - } + next_obj.map_or_else(|| Err(invalid_data("premature EOF")), |result| result.map_err(Into::into)) } fn yield_next_obj( @@ -56,14 +53,14 @@ fn yield_next_obj( } // https://stackoverflow.com/questions/68641157/how-can-i-stream-elements-from-inside-a-json-array-using-serde-json -pub(crate) fn json_iter_array( +pub fn json_iter_array( mut reader: R, ) -> impl Iterator> { let mut at_start = false; std::iter::from_fn(move || yield_next_obj(&mut reader, &mut at_start).transpose()) } -pub(crate) fn json_filter_file(file_path: &Path, filter: &HashMap<&str, &str>) -> Vec { +pub fn json_filter_file(file_path: &Path, filter: &HashMap<&str, &str>) -> Vec { let mut filtered: Vec = Vec::new(); if !file_path.exists() { return filtered; // Return early if the file does not exist @@ -75,15 +72,11 @@ pub(crate) fn json_filter_file(file_path: &Path, filter: &HashMap<&str, &str>) - for entry in json_iter_array::>(reader).flatten() { if let Some(item) = entry.as_object() { if filter.iter().all(|(&key, &value)| { - if let Some(field_value) = item.get(key) { - match field_value { + item.get(key).map_or(false, |field_value| match field_value { Value::String(s) => s == value, Value::Number(n) => value.parse::().ok() == n.as_i64(), _ => false, - } - } else { - false - } + }) }) { filtered.push(entry); } @@ -93,7 +86,7 @@ pub(crate) fn json_filter_file(file_path: &Path, filter: &HashMap<&str, &str>) - filtered } -pub(crate) fn json_write_documents_to_file(file: &Path, value: &T) -> Result<(), Error> +pub fn json_write_documents_to_file(file: &Path, value: &T) -> Result<(), Error> where T: ?Sized + Serialize, { diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 15589e2dc..c168debae 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,12 +1,12 @@ -pub (crate) mod file_utils; -pub (crate) mod request_utils; -pub (crate) mod download; -pub (crate) mod string_utils; -pub (crate) mod json_utils; -pub (crate) mod config_reader; -pub (crate) mod default_utils; -pub (crate) mod multi_file_reader; -pub (crate) mod file_lock_manager; -pub (crate) mod compressed_file_reader; +pub mod file_utils; +pub mod request_utils; +pub mod download; +pub mod string_utils; +pub mod json_utils; +pub mod config_reader; +pub mod default_utils; +pub mod multi_file_reader; +pub mod file_lock_manager; +pub mod compressed_file_reader; mod compression_utils; -pub (crate) mod directed_graph; \ No newline at end of file +pub mod directed_graph; \ No newline at end of file diff --git a/src/utils/multi_file_reader.rs b/src/utils/multi_file_reader.rs index c64a14e4b..dcbba2cf6 100644 --- a/src/utils/multi_file_reader.rs +++ b/src/utils/multi_file_reader.rs @@ -2,13 +2,13 @@ use std::fs::File; use std::io::{self, BufReader, ErrorKind, Read}; use std::path::{PathBuf}; -pub(crate) struct MultiFileReader { +pub struct MultiFileReader { files: Vec, current_reader: Option>, } impl MultiFileReader { - pub(crate) fn new(paths: &Vec) -> io::Result { + pub fn new(paths: &Vec) -> io::Result { let mut files = Vec::new(); for path in paths { match File::open(path) { diff --git a/src/utils/request_utils.rs b/src/utils/request_utils.rs index dbe0d4cea..749e624c4 100644 --- a/src/utils/request_utils.rs +++ b/src/utils/request_utils.rs @@ -21,11 +21,11 @@ use crate::repository::xtream_repository::FILE_EPG; use crate::utils::compression_utils::{ENCODING_DEFLATE, ENCODING_GZIP, is_deflate, is_gzip}; use crate::utils::file_utils::{get_file_path, persist_file}; -pub(crate) fn bytes_to_megabytes(bytes: u64) -> u64 { +pub const fn bytes_to_megabytes(bytes: u64) -> u64 { bytes / 1_048_576 } -pub(crate) async fn get_input_text_content_as_file(input: &ConfigInput, working_dir: &str, url_str: &str, persist_filepath: Option) -> Result { +pub async fn get_input_text_content_as_file(input: &ConfigInput, working_dir: &str, url_str: &str, persist_filepath: Option) -> Result { if log_enabled!(Level::Debug) { debug!("getting input text content working_dir: {}, url: {}", working_dir, url_str); } @@ -65,18 +65,16 @@ pub(crate) async fn get_input_text_content_as_file(input: &ConfigInput, working_ None => None }; - if let Some(path) = result { - Ok(path) - } else { + result.map_or_else(|| { let msg = format!("cant read input url: {url_str:?}"); error!("{}", msg); create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "{}", msg) - } + }, Ok) } } -pub(crate) async fn get_input_text_content(input: &ConfigInput, working_dir: &String, url_str: &str, persist_filepath: Option) -> Result { +pub async fn get_input_text_content(input: &ConfigInput, working_dir: &String, url_str: &str, persist_filepath: Option) -> Result { if log_enabled!(Level::Debug) { debug!("getting input text content working_dir: {}, url: {}", working_dir, url_str); } @@ -116,24 +114,22 @@ pub(crate) async fn get_input_text_content(input: &ConfigInput, working_dir: &St } None => None }; - if let Some(content) = result { - Ok(content) - } else { + result.map_or_else(|| { let msg = format!("cant read input url: {url_str:?}"); error!("{}", msg); create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "{}", msg) - } + }, Ok) } } -pub(crate) fn get_client_request(input: Option<&ConfigInput>, url: &Url, custom_headers: Option<&HashMap<&str, &[u8]>>) -> reqwest::RequestBuilder { +pub fn get_client_request(input: Option<&ConfigInput>, url: &Url, custom_headers: Option<&HashMap<&str, &[u8]>>) -> reqwest::RequestBuilder { let mut request = reqwest::Client::new().get(url.clone()); let headers = get_request_headers(input.map_or(&HashMap::new(), |i| &i.headers), custom_headers); request = request.headers(headers); request } -pub(crate) fn get_request_headers(defined_headers: &HashMap, custom_headers: Option<&HashMap<&str, &[u8]>>) -> HeaderMap { +pub fn get_request_headers(defined_headers: &HashMap, custom_headers: Option<&HashMap<&str, &[u8]>>) -> HeaderMap { let mut headers = HeaderMap::new(); for (key, value) in defined_headers { headers.insert( @@ -221,14 +217,7 @@ async fn get_remote_content(input: &ConfigInput, url: &Url) -> Result Some(value.to_string()), - Err(_) => None, - } - } else { - None - }; + let mut encoding = header_value.and_then(|encoding_header| encoding_header.to_str().map_or(None, |value| Some(value.to_string()))); match response.bytes().await { Ok(bytes) => { if bytes.len() >= 2 { @@ -283,30 +272,21 @@ async fn get_remote_content(input: &ConfigInput, url: &Url) -> Result) -> Result { +pub async fn download_text_content_as_file(input: &ConfigInput, url_str: &str, working_dir: &str, persist_filepath: Option) -> Result { if let Ok(url) = url_str.parse::() { if url.scheme() == "file" { - if let Ok(file_path) = url.to_file_path() { - if file_path.exists() { + url.to_file_path().map_or_else(|()| Err(Error::new(ErrorKind::Unsupported, format!("Unknown file {url}"))), |file_path| if file_path.exists() { Ok(file_path) } else { Err(Error::new(ErrorKind::NotFound, format!("Unknown file {file_path:?}"))) - } - } else { - Err(Error::new(ErrorKind::Unsupported, format!("Unknown file {url}"))) - } + }) } else { - let file_path = match persist_filepath { - None => { - match get_input_storage_path(input, working_dir) { - Ok(download_path) => { - Ok(download_path.join(FILE_EPG)) - } - Err(err) => Err(err) - } + let file_path = persist_filepath.map_or_else(|| match get_input_storage_path(input, working_dir) { + Ok(download_path) => { + Ok(download_path.join(FILE_EPG)) } - Some(path) => Ok(path), - }; + Err(err) => Err(err) + }, Ok); match file_path { Ok(persist_path) => get_remote_content_as_file(input, &url, &persist_path).await, Err(err) => Err(err) @@ -318,14 +298,10 @@ pub(crate) async fn download_text_content_as_file(input: &ConfigInput, url_str: } -pub(crate) async fn download_text_content(input: &ConfigInput, url_str: &str, persist_filepath: Option) -> Result { +pub async fn download_text_content(input: &ConfigInput, url_str: &str, persist_filepath: Option) -> Result { if let Ok(url) = url_str.parse::() { let result = if url.scheme() == "file" { - if let Ok(file_path) = url.to_file_path() { - get_local_file_content(&file_path) - } else { - Err(Error::new(ErrorKind::Other, format!("Unknown file {url}"))) - } + url.to_file_path().map_or_else(|()| Err(Error::new(ErrorKind::Other, format!("Unknown file {url}"))), |file_path| get_local_file_content(&file_path)) } else { get_remote_content(input, &url).await }; @@ -358,14 +334,14 @@ async fn download_json_content(input: &ConfigInput, url: &str, persist_filepath: } } -pub(crate) async fn get_input_json_content(input: &ConfigInput, url: &str, persist_filepath: Option) -> Result { +pub async fn get_input_json_content(input: &ConfigInput, url: &str, persist_filepath: Option) -> Result { match download_json_content(input, url, persist_filepath).await { Ok(content) => Ok(content), Err(e) => create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "cant download input url: {url} => {}", e) } } // -// pub(crate) fn get_base_url(url: &str) -> Option { +// pub fn get_base_url(url: &str) -> Option { // if let Some((scheme_end, rest)) = url.split_once("://") { // let scheme = scheme_end; // if let Some(authority_end) = rest.find('/') { diff --git a/src/utils/string_utils.rs b/src/utils/string_utils.rs index 22bad89e8..b8a8d87e5 100644 --- a/src/utils/string_utils.rs +++ b/src/utils/string_utils.rs @@ -1,6 +1,6 @@ // other implementations like calculating text_distance on all titles took too much time // we keep it now as simple as possible and less memory intensive. -pub (crate) fn get_title_group(text: &str) -> String { +pub fn get_title_group(text: &str) -> String { let alphabetic_only: String = text.chars().map(|c| if c.is_alphanumeric() { c } else { ' ' }).collect(); let parts = alphabetic_only.split_whitespace(); let mut combination = String::new(); @@ -13,7 +13,7 @@ pub (crate) fn get_title_group(text: &str) -> String { text.to_string() } -pub(crate) trait Capitalize { +pub trait Capitalize { fn capitalize(&self) -> String; } @@ -21,10 +21,7 @@ pub(crate) trait Capitalize { impl Capitalize for &str { fn capitalize(&self) -> String { let mut chars = self.chars(); - match chars.next() { - None => String::new(), // Handle empty string case - Some(first_char) => first_char.to_uppercase().collect::() + chars.as_str(), - } + chars.next().map_or_else(String::new, |first_char| first_char.to_uppercase().collect::() + chars.as_str()) } }