From fa1f48e201c2e096392c690d5da83cb4a7c8a6dd Mon Sep 17 00:00:00 2001 From: euzu Date: Thu, 30 Sep 2021 11:11:30 +0200 Subject: [PATCH] refactored, renamed processor to m3u-processing, moved function from main to utils --- src/config.rs | 38 ++++++++--------- src/m3u.rs | 35 +++++++--------- src/{processor.rs => m3u_processing.rs} | 5 +-- src/main.rs | 15 ++----- src/utils.rs | 55 ++++++++++++++----------- 5 files changed, 68 insertions(+), 80 deletions(-) rename src/{processor.rs => m3u_processing.rs} (93%) diff --git a/src/config.rs b/src/config.rs index 77f5eba37..e93721afd 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,16 +1,12 @@ -use regex::Regex; -use serde::{Deserialize, Serialize}; -use std::fmt; - -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, serde::Serialize, serde::Deserialize)] pub enum ItemField { Group, Name, Title, } -impl fmt::Display for ItemField { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl std::fmt::Display for ItemField { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match *self { ItemField::Group => write!(f, "Group"), ItemField::Name => write!(f, "Name"), @@ -19,22 +15,22 @@ impl fmt::Display for ItemField { } } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, serde::Serialize, serde::Deserialize)] pub enum FilterMode { Discard, Include, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct ConfigFilter { pub field: ItemField, pub pattern: String, #[serde(skip_serializing, skip_deserializing)] - pub re: Option, + pub re: Option, } impl ConfigFilter { - pub fn prepare(&mut self) -> () { + pub(crate) fn prepare(&mut self) -> () { let re = regex::Regex::new(&self.pattern); if re.is_err() { println!("cant parse regex: {}", &self.pattern); @@ -44,29 +40,29 @@ impl ConfigFilter { } } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct ConfigFilters { pub mode: FilterMode, pub rules: Vec, } impl ConfigFilters { - pub fn is_include(&self) -> bool { + pub(crate) fn is_include(&self) -> bool { matches!(self.mode, FilterMode::Include) } } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct ConfigRename { pub field: ItemField, pub pattern: String, pub new_name: String, #[serde(skip_serializing, skip_deserializing)] - pub re: Option, + pub re: Option, } impl ConfigRename { - pub fn prepare(&mut self) -> () { + pub(crate) fn prepare(&mut self) -> () { let re = regex::Regex::new(&self.pattern); if re.is_err() { println!("cant parse regex: {}", &self.pattern); @@ -77,33 +73,33 @@ impl ConfigRename { } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct ConfigTarget { pub filename: String, pub filter: ConfigFilters, pub rename: Vec, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct ConfigInput { pub url: String, pub persist: String, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct Config { pub input: ConfigInput, pub targets: Vec, } impl Config { - pub fn prepare(&mut self) { + pub(crate) fn prepare(&mut self) { for t in &mut self.targets { for f in &mut t.filter.rules { f.prepare(); } for r in &mut t.rename { - r.prepare(); + r.prepare(); } } } diff --git a/src/m3u.rs b/src/m3u.rs index beffb6fa8..afdde13c1 100644 --- a/src/m3u.rs +++ b/src/m3u.rs @@ -1,11 +1,6 @@ // https://de.wikipedia.org/wiki/M3U // https://siptv.eu/howto/playlist.html -use std::collections::hash_map::Entry; -use std::collections::HashMap; -use std::fmt; -use std::str::Chars; - pub struct PlaylistItemHeader { pub id: String, pub name: String, @@ -38,8 +33,8 @@ impl Clone for PlaylistItemHeader { } } -impl fmt::Display for PlaylistItemHeader { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { +impl std::fmt::Display for PlaylistItemHeader { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { fmt.write_str("id:")?; fmt.write_str(&self.id)?; fmt.write_str(", name:")?; @@ -80,8 +75,8 @@ impl Clone for PlaylistItem { } } -impl fmt::Display for PlaylistItem { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { +impl std::fmt::Display for PlaylistItem { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { fmt.write_str("header:")?; fmt.write_str(&self.header.to_string())?; fmt.write_str(", url:")?; @@ -91,7 +86,7 @@ impl fmt::Display for PlaylistItem { } impl PlaylistItem { - pub fn to_m3u(&self) -> String { + pub(crate) fn to_m3u(&self) -> String { let mut line = format!("#EXTINF:-1 tvg-id=\"{}\" tvg-name=\"{}\" tvg-logo=\"{}\" group-title=\"{}\"", self.header.id, self.header.name, self.header.logo, self.header.group); if !self.header.logo_small.is_empty() { line = format!("{} tvg-logo-small=\"!{}\"", line, self.header.logo_small); @@ -118,8 +113,8 @@ pub struct PlaylistGroup { pub channels: Vec, } -impl fmt::Display for PlaylistGroup { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { +impl std::fmt::Display for PlaylistGroup { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { fmt.write_str("title:")?; fmt.write_str(&self.title.to_string())?; fmt.write_str(", channels:")?; @@ -128,7 +123,7 @@ impl fmt::Display for PlaylistGroup { } } -fn token_value<'a>(it: &'a mut Chars) -> String { +fn token_value<'a>(it: &'a mut std::str::Chars) -> String { let oc = it.next(); if oc.is_some() && oc.unwrap() == '"' { return get_value(it); @@ -136,7 +131,7 @@ fn token_value<'a>(it: &'a mut Chars) -> String { return String::from(""); } -fn get_value<'a>(it: &'a mut Chars) -> String { +fn get_value<'a>(it: &'a mut std::str::Chars) -> String { let mut result: Vec = vec![]; let mut oc = it.next(); while oc.is_some() && oc.unwrap() != '"' { @@ -146,7 +141,7 @@ fn get_value<'a>(it: &'a mut Chars) -> String { return String::from(result.iter().collect::()); } -fn token_till(it: &mut Chars, stop_char: char) -> Option { +fn token_till(it: &mut std::str::Chars, stop_char: char) -> Option { let mut result: Vec = vec![]; loop { let c = it.next(); @@ -161,7 +156,7 @@ fn token_till(it: &mut Chars, stop_char: char) -> Option { if result.len() > 0 { Some(String::from(result.iter().collect::())) } else { None } } -fn skip_digit(it: &mut Chars) -> Option { +fn skip_digit(it: &mut std::str::Chars) -> Option { let mut c: Option; loop { c = it.next(); @@ -222,8 +217,8 @@ fn decode_header(content: &String) -> PlaylistItemHeader { plih } -pub fn decode(lines: &Vec) -> Vec { - let mut groups: HashMap> = HashMap::new(); +pub(crate) fn decode(lines: &Vec) -> Vec { + let mut groups: std::collections::HashMap> = std::collections::HashMap::new(); let mut sort_order: Vec = vec![]; let mut header: Option = None; let mut group: Option = None; @@ -248,11 +243,11 @@ pub fn decode(lines: &Vec) -> Vec { let key = String::from(&item.header.group); let key2 = String::from(&item.header.group); match groups.entry(key) { - Entry::Vacant(e) => { + std::collections::hash_map::Entry::Vacant(e) => { e.insert(vec![item]); sort_order.push(key2); } - Entry::Occupied(mut e) => { e.get_mut().push(item); } + std::collections::hash_map::Entry::Occupied(mut e) => { e.get_mut().push(item); } } } header = None; diff --git a/src/processor.rs b/src/m3u_processing.rs similarity index 93% rename from src/processor.rs rename to src/m3u_processing.rs index 749d830d1..85157db4d 100644 --- a/src/processor.rs +++ b/src/m3u_processing.rs @@ -1,12 +1,11 @@ -use std::fs::File; use std::io::Write; use crate::{config, m3u}; use crate::m3u::PlaylistItem; -pub fn write_m3u(playlist: &Vec, cfg: &config::Config) { +pub(crate) fn write_m3u(playlist: &Vec, cfg: &config::Config) { for t in &cfg.targets { - let mut file = match File::create(&t.filename) { + let mut file = match std::fs::File::create(&t.filename) { Ok(file) => file, Err(_) => { println!("cant open file: {}", t.filename); diff --git a/src/main.rs b/src/main.rs index 1b09350a0..a41bc1637 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,6 @@ -use std::path::{PathBuf}; mod m3u; mod config; -mod processor; +mod m3u_processing; mod utils; fn main() { @@ -40,17 +39,11 @@ fn main() { let input_arg = matches.value_of("input"); let url_str = input_arg.unwrap_or(if cfg.input.url.is_empty() { "playlist.m3u" } else { cfg.input.url.as_str() }); let persist_file = match input_arg { - Some(_) => matches.value_of("persist").map_or(None, |p|prepare_persist_path(p)), - None => if cfg.input.persist.is_empty() {None} else { prepare_persist_path(cfg.input.persist.as_str())}, + Some(_) => matches.value_of("persist").map_or(None, |p| utils::prepare_persist_path(p)), + None => if cfg.input.persist.is_empty() { None } else { utils::prepare_persist_path(cfg.input.persist.as_str()) }, }; let lines: Vec = utils::get_input_content(url_str, persist_file); let result = m3u::decode(&lines); - processor::write_m3u(&result, &cfg); -} - -fn prepare_persist_path(file_name: &str) -> Option { - let now = chrono::Local::now(); - let filename = file_name.replace("{}", now.format("%Y%m%d_%H%M%S").to_string().as_str()); - Some(PathBuf::from(filename)) + m3u_processing::write_m3u(&result, &cfg); } diff --git a/src/utils.rs b/src/utils.rs index aaf3a1d03..2679c0821 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,8 +1,6 @@ -use url::{Url}; use std::io::{BufRead, Write}; -use std::path::{PathBuf}; -pub fn get_default_config_path() -> String { +pub(crate) fn get_default_config_path() -> String { let default_path = std::path::Path::new("./"); let current_exe = std::env::current_exe(); let path: &std::path::Path = match current_exe { @@ -17,7 +15,7 @@ pub fn get_default_config_path() -> String { }) } -pub fn open_file(file_name: &str) -> std::fs::File { +pub(crate) fn open_file(file_name: &str) -> std::fs::File { let file = match std::fs::File::open(file_name) { Ok(file) => file, Err(_) => { @@ -28,8 +26,8 @@ pub fn open_file(file_name: &str) -> std::fs::File { file } -pub fn get_input_content(url_str: &str, persist_file: Option) -> Vec { - match url_str.parse::() { +pub(crate) fn get_input_content(url_str: &str, persist_file: Option) -> Vec { + match url_str.parse::() { Ok(url) => match download_content(url, persist_file) { Ok(content) => content, Err(e) => { @@ -43,11 +41,11 @@ pub fn get_input_content(url_str: &str, persist_file: Option) -> Vec {}, + Ok(_) => {} Err(e) => println!("cant persist to: {} => {}", to_file.to_str().unwrap_or("?"), e), } - } ; - std::io::BufReader::new(open_file(url_str)).lines(). map(|l| l.unwrap()).collect() + }; + std::io::BufReader::new(open_file(url_str)).lines().map(|l| l.unwrap()).collect() } else { println!("cant read input url: {} => {}", url_str, e); std::process::exit(1); @@ -56,38 +54,45 @@ pub fn get_input_content(url_str: &str, persist_file: Option) -> Vec) -> Result, String> { +fn download_content(url: url::Url, persist_file: Option) -> Result, String> { match reqwest::blocking::get(url) { Ok(response) => { if response.status().is_success() { match response.text_with_charset("utf8") { Ok(text) => { persists_playlist(persist_file, &text); - let result= text.lines().map(|l| String::from(l)).collect(); + let result = text.lines().map(|l| String::from(l)).collect(); Ok(result) - }, + } Err(e) => Err(e.to_string()) } } else { Err(format!("Request failed: {}", response.status())) } - }, + } Err(e) => Err(e.to_string()) } } -fn persists_playlist(persist_file: Option, text: &String) { - match persist_file { - Some(path_buf) => { - let filename = &path_buf.to_str().unwrap_or("?"); - match std::fs::File::create(&path_buf) { - Ok(mut file) => match file.write_all(text.as_bytes()) { - Ok(_) => println!("persisted: {}", filename), - Err(e) => println!("failed to persist file {}, {}", filename, e) - }, +fn persists_playlist(persist_file: Option, text: &String) { + match persist_file { + Some(path_buf) => { + let filename = &path_buf.to_str().unwrap_or("?"); + match std::fs::File::create(&path_buf) { + Ok(mut file) => match file.write_all(text.as_bytes()) { + Ok(_) => println!("persisted: {}", filename), Err(e) => println!("failed to persist file {}, {}", filename, e) - } - }, - None => {} + }, + Err(e) => println!("failed to persist file {}, {}", filename, e) + } + } + None => {} } } + +pub(crate) fn prepare_persist_path(file_name: &str) -> Option { + let now = chrono::Local::now(); + let filename = file_name.replace("{}", now.format("%Y%m%d_%H%M%S").to_string().as_str()); + Some(std::path::PathBuf::from(filename)) +} +