lints: Fixed the rest of the lints and some bugs along the way

This commit is contained in:
Chris Cochrun 2026-02-17 11:18:21 -06:00
parent 452f1fc0c7
commit 02773f068f
16 changed files with 299 additions and 416 deletions

View file

@ -13,6 +13,7 @@ use tar::Builder;
use tracing::error;
use zstd::Encoder;
#[allow(clippy::too_many_lines)]
pub fn save(
list: Vec<ServiceItem>,
path: impl AsRef<Path>,
@ -23,7 +24,7 @@ pub fn save(
fs::remove_file(path).into_diagnostic()?;
}
let save_file = File::create(path).into_diagnostic()?;
let ron = process_service_items(&list)?;
let ron = process_service_items(&list);
let encoder = Encoder::new(save_file, 3)
.expect("file encoder shouldn't fail")
@ -58,7 +59,7 @@ pub fn save(
}
}
match tar.append_file("serviceitems.ron", &mut f) {
Ok(_) => {
Ok(()) => {
dbg!(
"should have added serviceitems.ron to the file"
);
@ -125,7 +126,7 @@ pub fn save(
}
match tar.finish() {
Ok(_) => (),
Ok(()) => (),
Err(e) => {
error!(?e);
dbg!(&e);
@ -135,11 +136,11 @@ pub fn save(
fs::remove_dir_all(temp_dir).into_diagnostic()
}
fn process_service_items(items: &Vec<ServiceItem>) -> Result<String> {
Ok(items
fn process_service_items(items: &[ServiceItem]) -> String {
items
.iter()
.filter_map(|item| ron::ser::to_string(item).ok())
.collect())
.collect()
}
#[cfg(test)]

View file

@ -72,11 +72,10 @@ impl Content for Image {
fn subtext(&self) -> String {
if self.path.exists() {
self.path
.file_name()
.map_or("Missing image".into(), |f| {
f.to_string_lossy().to_string()
})
self.path.file_name().map_or_else(
|| "Missing image".into(),
|f| f.to_string_lossy().to_string(),
)
} else {
"Missing image".into()
}
@ -89,6 +88,7 @@ impl From<Value> for Image {
}
}
#[allow(clippy::option_if_let_else)]
impl From<&Value> for Image {
fn from(value: &Value) -> Self {
match value {

View file

@ -28,9 +28,11 @@ impl TryFrom<PathBuf> for ServiceItemKind {
let ext = path
.extension()
.and_then(|ext| ext.to_str())
.ok_or(miette::miette!(
"There isn't an extension on this file"
))?;
.ok_or_else(|| {
miette::miette!(
"There isn't an extension on this file"
)
})?;
match ext {
"png" | "jpg" | "jpeg" => {
Ok(Self::Image(Image::from(path)))

View file

@ -1,4 +1,4 @@
use std::{borrow::Cow, mem::replace, path::PathBuf};
use std::{borrow::Cow, fs, mem::replace, path::PathBuf};
use cosmic::iced::clipboard::mime::{AllowedMimeTypes, AsMimeTypes};
use miette::{IntoDiagnostic, Result, miette};
@ -84,26 +84,36 @@ impl<T> Model<T> {
}
pub fn update_item(&mut self, item: T, index: i32) -> Result<()> {
if let Some(current_item) = self.items.get_mut(index as usize)
{
let _old_item = replace(current_item, item);
Ok(())
} else {
Err(miette!(
"Item doesn't exist in model. Id was {}",
index
))
}
self.items
.get_mut(
usize::try_from(index)
.expect("Shouldn't be negative"),
)
.map_or_else(
|| {
Err(miette!(
"Item doesn't exist in model. Id was {index}"
))
},
|current_item| {
let _old_item = replace(current_item, item);
Ok(())
},
)
}
pub fn remove_item(&mut self, index: i32) -> Result<()> {
self.items.remove(index as usize);
self.items.remove(
usize::try_from(index).expect("Shouldn't be negative"),
);
Ok(())
}
#[must_use]
pub fn get_item(&self, index: i32) -> Option<&T> {
self.items.get(index as usize)
self.items.get(
usize::try_from(index).expect("shouldn't be negative"),
)
}
pub fn find<P>(&self, f: P) -> Option<&T>
@ -114,7 +124,10 @@ impl<T> Model<T> {
}
pub fn insert_item(&mut self, item: T, index: i32) -> Result<()> {
self.items.insert(index as usize, item);
self.items.insert(
usize::try_from(index).expect("Shouldn't be negative"),
item,
);
Ok(())
}
}
@ -131,11 +144,13 @@ impl<T> Model<T> {
// }
pub async fn get_db() -> SqliteConnection {
let mut data = dirs::data_local_dir().unwrap();
let mut data = dirs::data_local_dir()
.expect("Should be able to find a data dir");
data.push("lumina");
let _ = fs::create_dir_all(&data);
data.push("library-db.sqlite3");
let mut db_url = String::from("sqlite://");
db_url.push_str(data.to_str().unwrap());
db_url.push_str(data.to_str().expect("Should be there"));
SqliteConnection::connect(&db_url).await.expect("problems")
}

View file

@ -65,27 +65,24 @@ impl From<PathBuf> for Presentation {
.to_str()
.unwrap_or_default()
{
"pdf" => {
if let Ok(document) = Document::open(&value.as_path())
{
if let Ok(count) = document.page_count() {
PresKind::Pdf {
starting_index: 0,
ending_index: count - 1,
}
} else {
"pdf" => Document::open(&value.as_path()).map_or(
PresKind::Pdf {
starting_index: 0,
ending_index: 0,
},
|document| {
document.page_count().map_or(
PresKind::Pdf {
starting_index: 0,
ending_index: 0,
}
}
} else {
PresKind::Pdf {
starting_index: 0,
ending_index: 0,
}
}
}
},
|count| PresKind::Pdf {
starting_index: 0,
ending_index: count - 1,
},
)
},
),
"html" => PresKind::Html,
_ => PresKind::Generic,
};
@ -129,11 +126,10 @@ impl Content for Presentation {
fn subtext(&self) -> String {
if self.path.exists() {
self.path
.file_name()
.map_or("Missing presentation".into(), |f| {
f.to_string_lossy().to_string()
})
self.path.file_name().map_or_else(
|| "Missing presentation".into(),
|f| f.to_string_lossy().to_string(),
)
} else {
"Missing presentation".into()
}
@ -146,6 +142,7 @@ impl From<Value> for Presentation {
}
}
#[allow(clippy::option_if_let_else)]
impl From<&Value> for Presentation {
fn from(value: &Value) -> Self {
match value {
@ -206,15 +203,14 @@ impl ServiceTrait for Presentation {
let pages: Vec<Handle> = pages
.enumerate()
.filter_map(|(index, page)| {
if (index as i32) < starting_index {
return None;
} else if (index as i32) > ending_index {
let index = i32::try_from(index)
.expect("Shouldn't be that high");
if index < starting_index || index > ending_index {
return None;
}
let Some(page) = page.ok() else {
return None;
};
let page = page.ok()?;
let matrix = Matrix::IDENTITY;
let colorspace = Colorspace::device_rgb();
let Ok(pixmap) = page
@ -248,7 +244,10 @@ impl ServiceTrait for Presentation {
.video_loop(false)
.video_start_time(0.0)
.video_end_time(0.0)
.pdf_index(index as u32)
.pdf_index(
u32::try_from(index)
.expect("Shouldn't get that high"),
)
.pdf_page(page)
.build()?;
slides.push(slide);
@ -334,32 +333,38 @@ impl Model<Presentation> {
presentation.ending_index,
) {
PresKind::Pdf {
starting_index: starting_index as i32,
ending_index: ending_index as i32,
starting_index: i32::try_from(
starting_index,
)
.expect("Shouldn't get that high"),
ending_index: i32::try_from(
ending_index,
)
.expect("Shouldn't get that high"),
}
} else {
let path =
PathBuf::from(presentation.path);
if let Ok(document) =
Document::open(path.as_path())
{
if let Ok(count) =
document.page_count()
{
let ending_index = count - 1;
PresKind::Pdf {
starting_index: 0,
ending_index,
}
} else {
PresKind::Pdf {
starting_index: 0,
ending_index: 0,
}
}
} else {
PresKind::Generic
}
Document::open(path.as_path()).map_or(
PresKind::Generic,
|document| {
document.page_count().map_or(
PresKind::Pdf {
starting_index: 0,
ending_index: 0,
},
|count| {
let ending_index =
count - 1;
PresKind::Pdf {
starting_index: 0,
ending_index,
}
},
)
},
)
},
});
}
@ -416,16 +421,16 @@ pub async fn update_presentation_in_db(
.unwrap_or_default();
let html = presentation.kind == PresKind::Html;
let mut db = db.detach();
let mut starting_index = 0;
let mut ending_index = 0;
if let PresKind::Pdf {
let (starting_index, ending_index) = if let PresKind::Pdf {
starting_index: s_index,
ending_index: e_index,
} = presentation.get_kind()
} =
presentation.get_kind()
{
starting_index = *s_index;
ending_index = *e_index;
}
(*s_index, *e_index)
} else {
(0, 0)
};
let id = presentation.id;
if let Err(e) =
query!("SELECT id FROM presentations where id = $1", id)

View file

@ -32,7 +32,7 @@ impl Eq for ServiceItem {}
impl PartialOrd for ServiceItem {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.id.partial_cmp(&other.id)
Some(self.cmp(other))
}
}
@ -89,9 +89,11 @@ impl TryFrom<PathBuf> for ServiceItem {
let ext = path
.extension()
.and_then(|ext| ext.to_str())
.ok_or(miette::miette!(
"There isn't an extension on this file"
))?;
.ok_or_else(|| {
miette::miette!(
"There isn't an extension on this file"
)
})?;
match ext {
"png" | "jpg" | "jpeg" => {
Ok(Self::from(&Image::from(path)))
@ -157,6 +159,8 @@ impl From<Value> for ServiceItem {
}
}
#[allow(clippy::option_if_let_else)]
#[allow(clippy::match_like_matches_macro)]
impl From<&Value> for ServiceItem {
fn from(value: &Value) -> Self {
match value {
@ -280,64 +284,61 @@ impl From<Vec<ServiceItem>> for Service {
impl From<&Song> for ServiceItem {
fn from(song: &Song) -> Self {
if let Ok(slides) = song.to_slides() {
Self {
song.to_slides().map_or_else(
|_| Self {
kind: ServiceItemKind::Song(song.clone()),
database_id: song.id,
title: song.title.clone(),
..Default::default()
},
|slides| Self {
kind: ServiceItemKind::Song(song.clone()),
database_id: song.id,
title: song.title.clone(),
slides,
..Default::default()
}
} else {
Self {
kind: ServiceItemKind::Song(song.clone()),
database_id: song.id,
title: song.title.clone(),
..Default::default()
}
}
},
)
}
}
impl From<&Video> for ServiceItem {
fn from(video: &Video) -> Self {
if let Ok(slides) = video.to_slides() {
Self {
video.to_slides().map_or_else(
|_| Self {
kind: ServiceItemKind::Video(video.clone()),
database_id: video.id,
title: video.title.clone(),
..Default::default()
},
|slides| Self {
kind: ServiceItemKind::Video(video.clone()),
database_id: video.id,
title: video.title.clone(),
slides,
..Default::default()
}
} else {
Self {
kind: ServiceItemKind::Video(video.clone()),
database_id: video.id,
title: video.title.clone(),
..Default::default()
}
}
},
)
}
}
impl From<&Image> for ServiceItem {
fn from(image: &Image) -> Self {
if let Ok(slides) = image.to_slides() {
Self {
image.to_slides().map_or_else(
|_| Self {
kind: ServiceItemKind::Image(image.clone()),
database_id: image.id,
title: image.title.clone(),
..Default::default()
},
|slides| Self {
kind: ServiceItemKind::Image(image.clone()),
database_id: image.id,
title: image.title.clone(),
slides,
..Default::default()
}
} else {
Self {
kind: ServiceItemKind::Image(image.clone()),
database_id: image.id,
title: image.title.clone(),
..Default::default()
}
}
},
)
}
}
@ -370,13 +371,9 @@ impl From<&Presentation> for ServiceItem {
#[allow(unused)]
impl Service {
fn add_item(
&mut self,
item: impl Into<ServiceItem>,
) -> Result<()> {
fn add_item(&mut self, item: impl Into<ServiceItem>) {
let service_item: ServiceItem = item.into();
self.items.push(service_item);
Ok(())
}
pub fn to_slides(&self) -> Result<Vec<Slide>> {
@ -392,7 +389,7 @@ impl Service {
.collect::<Vec<Slide>>();
let mut final_slides = vec![];
for (index, mut slide) in slides.into_iter().enumerate() {
slide.set_index(index as i32);
slide.set_index(i32::try_from(index).into_diagnostic()?);
final_slides.push(slide);
}
Ok(final_slides)
@ -455,19 +452,15 @@ mod test {
let pres = test_presentation();
let pres_item = ServiceItem::from(&pres);
let mut service_model = Service::default();
match service_model.add_item(&song) {
Ok(_) => {
assert_eq!(
ServiceItemKind::Song(song),
service_model.items[0].kind
);
assert_eq!(
ServiceItemKind::Presentation(pres),
pres_item.kind
);
assert_eq!(service_item, service_model.items[0]);
}
Err(e) => panic!("Problem adding item: {:?}", e),
}
service_model.add_item(&song);
assert_eq!(
ServiceItemKind::Song(song),
service_model.items[0].kind
);
assert_eq!(
ServiceItemKind::Presentation(pres),
pres_item.kind
);
assert_eq!(service_item, service_model.items[0]);
}
}

View file

@ -136,12 +136,15 @@ impl TryFrom<PathBuf> for Background {
type Error = ParseError;
fn try_from(path: PathBuf) -> Result<Self, Self::Error> {
let path = if path.starts_with("~") {
let path = path.to_str().unwrap().to_string();
let path = path
.to_str()
.expect("Should have a string")
.to_string();
let path = path.trim_start_matches("file://");
let home = dirs::home_dir()
.unwrap()
.expect("We should have a home directory")
.to_str()
.unwrap()
.expect("Gah")
.to_string();
let path = path.replace('~', &home);
PathBuf::from(path)
@ -189,16 +192,18 @@ impl TryFrom<&str> for Background {
fn try_from(value: &str) -> Result<Self, Self::Error> {
let value = value.trim_start_matches("file://");
if value.starts_with('~') {
if let Some(home) = dirs::home_dir() {
if let Some(home) = home.to_str() {
let value = value.replace('~', home);
Self::try_from(PathBuf::from(value))
} else {
Self::try_from(PathBuf::from(value))
}
} else {
Self::try_from(PathBuf::from(value))
}
dirs::home_dir().map_or_else(
|| Self::try_from(PathBuf::from(value)),
|home| {
home.to_str().map_or_else(
|| Self::try_from(PathBuf::from(value)),
|home| {
let value = value.replace('~', home);
Self::try_from(PathBuf::from(value))
},
)
},
)
} else if value.starts_with("./") {
Err(ParseError::CannotCanonicalize)
} else {
@ -262,95 +267,116 @@ impl From<&Slide> for Value {
}
impl Slide {
#[must_use]
pub fn set_text(mut self, text: impl AsRef<str>) -> Self {
self.text = text.as_ref().into();
self
}
#[must_use]
pub fn with_text_svg(mut self, text_svg: TextSvg) -> Self {
self.text_svg = Some(text_svg);
self
}
#[must_use]
pub fn set_font(mut self, font: impl AsRef<str>) -> Self {
self.font = Some(font.as_ref().into());
self
}
#[must_use]
pub const fn set_font_size(mut self, font_size: i32) -> Self {
self.font_size = font_size;
self
}
#[must_use]
pub fn set_audio(mut self, audio: Option<PathBuf>) -> Self {
self.audio = audio;
self
}
#[must_use]
pub const fn set_pdf_index(mut self, pdf_index: u32) -> Self {
self.pdf_index = pdf_index;
self
}
#[must_use]
pub const fn set_stroke(mut self, stroke: Stroke) -> Self {
self.stroke = Some(stroke);
self
}
#[must_use]
pub const fn set_shadow(mut self, shadow: Shadow) -> Self {
self.shadow = Some(shadow);
self
}
#[must_use]
pub const fn set_text_color(mut self, color: Color) -> Self {
self.text_color = Some(color);
self
}
#[must_use]
pub const fn background(&self) -> &Background {
&self.background
}
#[must_use]
pub fn text(&self) -> String {
self.text.clone()
}
#[must_use]
pub const fn text_alignment(&self) -> TextAlignment {
self.text_alignment
}
#[must_use]
pub const fn font_size(&self) -> i32 {
self.font_size
}
#[must_use]
pub fn font(&self) -> Option<Font> {
self.font.clone()
}
#[must_use]
pub const fn video_loop(&self) -> bool {
self.video_loop
}
#[must_use]
pub fn audio(&self) -> Option<PathBuf> {
self.audio.clone()
}
#[must_use]
pub fn pdf_page(&self) -> Option<Handle> {
self.pdf_page.clone()
}
#[must_use]
pub fn text_color(&self) -> Option<Color> {
self.text_color.clone()
}
#[must_use]
pub fn stroke(&self) -> Option<Stroke> {
self.stroke.clone()
}
#[must_use]
pub fn shadow(&self) -> Option<Shadow> {
self.shadow.clone()
}
#[must_use]
pub const fn pdf_index(&self) -> u32 {
self.pdf_index
}
@ -405,7 +431,8 @@ impl From<&Value> for Slide {
}
}
fn lisp_to_slide(lisp: &Vec<Value>) -> Slide {
#[allow(clippy::option_if_let_else)]
fn lisp_to_slide(lisp: &[Value]) -> Slide {
const DEFAULT_BACKGROUND_LOCATION: usize = 1;
const DEFAULT_TEXT_LOCATION: usize = 0;
@ -469,6 +496,7 @@ fn lisp_to_slide(lisp: &Vec<Value>) -> Slide {
}
}
#[allow(clippy::option_if_let_else)]
fn lisp_to_font_size(lisp: &Value) -> i32 {
match lisp {
Value::List(list) => {
@ -501,6 +529,7 @@ fn lisp_to_text(lisp: &Value) -> impl Into<String> {
// Need to return a Result here so that we can propogate
// errors and then handle them appropriately
#[allow(clippy::option_if_let_else)]
pub fn lisp_to_background(lisp: &Value) -> Background {
match lisp {
Value::List(list) => {

View file

@ -35,10 +35,7 @@ pub async fn search_online_song_links(
Ok(document
.select(&best_matches_selector)
.filter_map(|best_section| {
Some(best_section.select(&lyric_selector))
})
.flatten()
.flat_map(|best_section| best_section.select(&lyric_selector))
.map(|a| {
a.value().attr("href").unwrap_or("").trim().to_string()
})
@ -52,13 +49,17 @@ pub async fn search_online_song_links(
.collect())
}
// leaving this lint unfixed because I don't know if we will need this
// id value or not in the future and I'd like to keep the code understanding
// of what this variable might be.
#[allow(clippy::no_effect_underscore_binding)]
pub async fn link_to_online_song(
links: Vec<impl AsRef<str> + std::fmt::Display>,
) -> Result<Vec<OnlineSong>> {
let mut songs = vec![];
for link in links {
let parts = link
.to_string()
.as_ref()
.split('/')
.map(std::string::ToString::to_string)
.collect::<Vec<String>>();

View file

@ -85,8 +85,8 @@ pub enum VerseName {
impl VerseName {
#[must_use]
pub fn from_string(name: String) -> Self {
match name.as_str() {
pub fn from_string(name: &str) -> Self {
match name {
"Verse" => Self::Verse { number: 1 },
"Pre-Chorus" => Self::PreChorus { number: 1 },
"Chorus" => Self::Chorus { number: 1 },
@ -96,7 +96,7 @@ impl VerseName {
"Outro" => Self::Outro { number: 1 },
"Instrumental" => Self::Instrumental { number: 1 },
"Other" => Self::Other { number: 1 },
"Blank" => Self::Blank,
// Blank is included in wildcard
_ => Self::Blank,
}
}
@ -260,7 +260,9 @@ impl Content for Song {
}
fn subtext(&self) -> String {
self.author.clone().unwrap_or("Author missing".into())
self.author
.clone()
.unwrap_or_else(|| "Author missing".into())
}
}
@ -278,7 +280,9 @@ impl ServiceTrait for Song {
let lyrics: Vec<String> = self
.verses
.as_ref()
.ok_or(miette!("There are no verses assigned yet."))?
.ok_or_else(|| {
miette!("There are no verses assigned yet.")
})?
.iter()
.filter_map(|verse| self.get_lyric(verse))
.flat_map(|lyric| {
@ -299,13 +303,12 @@ impl ServiceTrait for Song {
.clone()
.unwrap_or_else(|| "Calibri".into()),
)
.style(
self.font_style.clone().unwrap_or_default(),
)
.weight(
self.font_weight.clone().unwrap_or_default(),
)
.size(self.font_size.unwrap_or(100) as u8);
.style(self.font_style.unwrap_or_default())
.weight(self.font_weight.unwrap_or_default())
.size(
u8::try_from(self.font_size.unwrap_or(100))
.unwrap_or(100),
);
let stroke_size =
self.stroke_size.unwrap_or_default();
let stroke: Stroke = stroke(
@ -367,22 +370,11 @@ impl ServiceTrait for Song {
}
}
#[allow(clippy::too_many_lines)]
impl FromRow<'_, SqliteRow> for Song {
fn from_row(row: &SqliteRow) -> sqlx::Result<Self> {
let lyrics: &str = row.try_get("lyrics")?;
// let Some((mut verses, mut verse_map)) =
// lyrics_to_verse(lyrics.clone()).ok()
// else {
// return Err(sqlx::Error::ColumnDecode {
// index: "8".into(),
// source: miette!(
// "Couldn't decode the song into verses"
// )
// .into(),
// });
// };
let Ok(verse_map) = ron::de::from_str::<
Option<HashMap<VerseName, String>>,
>(lyrics) else {
@ -508,6 +500,9 @@ impl From<Value> for Song {
}
}
#[allow(clippy::option_if_let_else)]
#[allow(clippy::needless_pass_by_value)]
#[allow(clippy::too_many_lines)]
pub fn lisp_to_song(list: Vec<Value>) -> Song {
const DEFAULT_SONG_ID: i32 = 0;
// const DEFAULT_SONG_LOCATION: usize = 0;
@ -586,7 +581,8 @@ pub fn lisp_to_song(list: Vec<Value>) -> Song {
.position(|v| v == &Value::Keyword(Keyword::from("title")))
{
let pos = key_pos + 1;
list.get(pos).map_or(String::from("song"), String::from)
list.get(pos)
.map_or_else(|| String::from("song"), String::from)
} else {
String::from("song")
};
@ -629,10 +625,7 @@ pub fn lisp_to_song(list: Vec<Value>) -> Song {
|| text.contains("i1")
}
_ => false,
} && match &inner[1] {
Value::String(_) => true,
_ => false,
})
} && matches!(&inner[1], Value::String(_)))
}
_ => false,
})
@ -654,7 +647,7 @@ pub fn lisp_to_song(list: Vec<Value>) -> Song {
let verse_title = match lyric_verse.as_str() {
"i1" => r"\n\nIntro 1\n",
"i2" => r"\n\nIntro 1\n",
"i2" => r"\n\nIntro 2\n",
"v1" => r"\n\nVerse 1\n",
"v2" => r"\n\nVerse 2\n",
"v3" => r"\n\nVerse 3\n",
@ -722,7 +715,7 @@ impl Model<Song> {
pub async fn load_from_db(&mut self, db: &mut SqlitePool) {
// static DATABASE_URL: &str = "sqlite:///home/chris/.local/share/lumina/library-db.sqlite3";
let db1 = db.acquire().await.unwrap();
let db1 = db.acquire().await.expect("Database not found");
let result = query("SELECT verse_order, font_size, background_type, horizontal_text_alignment, vertical_text_alignment, title, font, background, lyrics, ccli, author, audio, stroke_size, shadow_size, stroke_color, shadow_color, shadow_offset_x, shadow_offset_y, id from songs").fetch_all(&mut db1.detach()).await;
match result {
Ok(s) => {
@ -766,16 +759,14 @@ pub async fn add_song_to_db(
let mut song = Song::default();
let verse_order = {
if let Some(vo) = song.verse_order.clone() {
song.verse_order.clone().map_or_else(String::new, |vo| {
vo.into_iter()
.map(|mut s| {
s.push(' ');
s
})
.collect::<String>()
} else {
String::new()
}
})
};
let audio = song
@ -803,7 +794,9 @@ pub async fn add_song_to_db(
.execute(&mut db)
.await
.into_diagnostic()?;
song.id = res.last_insert_rowid() as i32;
song.id = i32::try_from(res.last_insert_rowid()).expect(
"Fairly confident that this number won't get that high",
);
Ok(song)
}
@ -921,7 +914,7 @@ impl Song {
verse_map
.entry(*verse)
.and_modify(|old_lyrics| {
*old_lyrics = lyric_copy.clone();
old_lyrics.clone_from(&lyric_copy);
})
.or_insert(lyric_copy);
// debug!(?verse_map, "should be updated");
@ -935,10 +928,6 @@ impl Song {
}
pub fn get_lyrics(&self) -> Result<Vec<String>> {
// ---------------------------------
// new implementation
// ---------------------------------
if let Some(verses) = self.verses.as_ref() {
let mut lyrics = vec![];
for verse in verses {
@ -952,73 +941,7 @@ impl Song {
}
return Ok(lyrics);
}
return Err(miette!("No verses in this song yet"));
// ---------------------------------
// old implementation
// ---------------------------------
// let mut lyric_list = Vec::new();
// if self.lyrics.is_none() {
// return Err(miette!("There is no lyrics here"));
// } else if self.verse_order.is_none() {
// return Err(miette!("There is no verse_order here"));
// } else if self
// .verse_order
// .clone()
// .is_some_and(|v| v.is_empty())
// {
// return Err(miette!("There is no verse_order here"));
// }
// if let Some(raw_lyrics) = self.lyrics.clone() {
// let raw_lyrics = raw_lyrics.as_str();
// let verse_order = self.verses.clone();
// let mut lyric_map = HashMap::new();
// let mut verse_title = String::new();
// let mut lyric = String::new();
// for (i, line) in raw_lyrics.split('\n').enumerate() {
// if VERSE_KEYWORDS.contains(&line) {
// if i != 0 {
// lyric_map.insert(verse_title, lyric);
// lyric = String::new();
// verse_title = line.to_string();
// } else {
// verse_title = line.to_string();
// }
// } else {
// lyric.push_str(line);
// lyric.push('\n');
// }
// }
// lyric_map.insert(verse_title, lyric);
// for verse in verse_order.unwrap_or_default() {
// let verse_name = &verse.get_name();
// if let Some(lyric) = lyric_map.get(verse_name) {
// if lyric.contains("\n\n") {
// let split_lyrics: Vec<&str> =
// lyric.split("\n\n").collect();
// for lyric in split_lyrics {
// if lyric.is_empty() {
// continue;
// }
// lyric_list.push(lyric.to_string());
// }
// continue;
// }
// lyric_list.push(lyric.clone());
// } else {
// // error!("NOT WORKING!");
// }
// }
// // for lyric in lyric_list.iter() {
// // debug!(lyric = ?lyric)
// // }
// Ok(lyric_list)
// } else {
// Err(miette!("There are no lyrics"))
// }
Err(miette!("No verses in this song yet"))
}
pub fn update_verse_name(
@ -1123,39 +1046,28 @@ impl Song {
#[must_use]
pub fn get_next_verse_name(&self) -> VerseName {
if let Some(verse_names) = &self.verses {
let verses: Vec<&VerseName> = verse_names
let verses = verse_names
.iter()
.filter(|verse| match verse {
VerseName::Verse { .. } => true,
_ => false,
.filter(|verse| {
matches!(verse, VerseName::Verse { .. })
})
.sorted()
.collect();
let choruses: Vec<&VerseName> = verse_names
.iter()
.filter(|verse| match verse {
VerseName::Chorus { .. } => true,
_ => false,
})
.collect();
let bridges: Vec<&VerseName> = verse_names
.iter()
.filter(|verse| match verse {
VerseName::Bridge { .. } => true,
_ => false,
})
.collect();
if verses.is_empty() {
.sorted();
let mut choruses = verse_names.iter().filter(|verse| {
matches!(verse, VerseName::Chorus { .. })
});
let mut bridges = verse_names.iter().filter(|verse| {
matches!(verse, VerseName::Bridge { .. })
});
if verses.len() == 0 {
VerseName::Verse { number: 1 }
} else if choruses.is_empty() {
} else if choruses.next().is_none() {
VerseName::Chorus { number: 1 }
} else if verses.len() == 1 {
let verse_number =
if let Some(last_verse) = verses.iter().last() {
match last_verse {
VerseName::Verse { number } => *number,
_ => 0,
}
if let Some(VerseName::Verse { number }) =
verses.last()
{
*number
} else {
0
};
@ -1163,10 +1075,10 @@ impl Song {
return VerseName::Verse { number: 1 };
}
VerseName::Verse { number: 2 }
} else if bridges.is_empty() {
} else if bridges.next().is_none() {
VerseName::Bridge { number: 1 }
} else {
if let Some(last_verse) = verses.iter().last()
if let Some(last_verse) = verses.last()
&& let VerseName::Verse { number } = last_verse
{
return VerseName::Verse { number: number + 1 };
@ -1194,17 +1106,15 @@ impl Song {
pub(crate) fn verse_name_from_str(
&self,
verse_name: String, // chorus 2
verse_name: &str, // chorus 2
old_verse_name: VerseName, // v4
) -> VerseName {
if old_verse_name.get_name() == verse_name {
return old_verse_name;
}
if let Some(verses) =
self.verse_map.clone().map(|verse_map| {
self.verse_map.clone().map(|verse_map| {
verse_map.into_keys().collect::<Vec<VerseName>>()
})
{
}).map_or_else(|| VerseName::from_string(verse_name), |verses| {
verses
.into_iter()
.filter(|verse| {
@ -1212,7 +1122,7 @@ impl Song {
.get_name()
.split_whitespace()
.next()
.unwrap()
.expect("Shouldn't fail, the get_name() fn won't return a string that is blank or all whitespace")
== verse_name
})
.sorted()
@ -1221,9 +1131,7 @@ impl Song {
|| VerseName::from_string(verse_name),
|verse_name| verse_name.next(),
)
} else {
VerseName::from_string(verse_name)
}
})
}
pub(crate) fn delete_verse(&mut self, verse: VerseName) {

View file

@ -90,6 +90,7 @@ impl From<Value> for Video {
}
}
#[allow(clippy::cast_precision_loss)]
impl From<&Value> for Video {
fn from(value: &Value) -> Self {
match value {

View file

@ -41,7 +41,6 @@ use miette::{IntoDiagnostic, Result, miette};
use rayon::prelude::*;
use resvg::usvg::fontdb;
use std::collections::HashMap;
use std::fs::read_to_string;
use std::path::PathBuf;
use std::sync::Arc;
use tracing::{debug, level_filters::LevelFilter};

View file

@ -27,6 +27,7 @@ use cosmic::{
menu, mouse_area, responsive, scrollable, text,
},
};
use derive_more::Debug;
use iced_video_player::{Position, Video, VideoPlayer, gst_pbutils};
use rodio::{Decoder, OutputStream, OutputStreamBuilder, Sink};
use tracing::{debug, error, info, warn};
@ -41,7 +42,7 @@ use crate::{
},
};
const REFERENCE_WIDTH: f32 = 1920.0;
// const REFERENCE_WIDTH: f32 = 1920.0;
static DEFAULT_SLIDE: LazyLock<Slide> = LazyLock::new(Slide::default);
// #[derive(Default, Clone, Debug)]
@ -73,8 +74,8 @@ pub(crate) enum Action {
None,
}
#[derive(Clone)]
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug)]
pub(crate) enum Message {
NextSlide,
PrevSlide,
@ -83,7 +84,7 @@ pub(crate) enum Message {
ClickSlide(usize, usize),
EndVideo,
StartVideo,
StartAudio,
// StartAudio,
EndAudio,
VideoPos(f32),
VideoFrame,
@ -95,80 +96,19 @@ pub(crate) enum Message {
RightClickSlide(usize, usize),
AssignObsScene(usize),
UpdateObsScenes(Vec<Scene>),
#[debug("AddObsClient")]
AddObsClient(Arc<Client>),
AssignSlideAction(slide_actions::Action),
}
impl std::fmt::Debug for Message {
fn fmt(
&self,
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
match self {
Self::NextSlide => write!(f, "NextSlide"),
Self::PrevSlide => write!(f, "PrevSlide"),
Self::SlideChange(arg0) => {
f.debug_tuple("SlideChange").field(arg0).finish()
}
Self::ActivateSlide(arg0, arg1) => f
.debug_tuple("ActivateSlide")
.field(arg0)
.field(arg1)
.finish(),
Self::ClickSlide(arg0, arg1) => f
.debug_tuple("ClickSlide")
.field(arg0)
.field(arg1)
.finish(),
Self::EndVideo => write!(f, "EndVideo"),
Self::StartVideo => write!(f, "StartVideo"),
Self::StartAudio => write!(f, "StartAudio"),
Self::EndAudio => write!(f, "EndAudio"),
Self::VideoPos(arg0) => {
f.debug_tuple("VideoPos").field(arg0).finish()
}
Self::VideoFrame => write!(f, "VideoFrame"),
Self::MissingPlugin(arg0) => {
f.debug_tuple("MissingPlugin").field(arg0).finish()
}
Self::HoveredSlide(arg0) => {
f.debug_tuple("HoveredSlide").field(arg0).finish()
}
Self::ChangeFont(arg0) => {
f.debug_tuple("ChangeFont").field(arg0).finish()
}
Self::Error(arg0) => {
f.debug_tuple("Error").field(arg0).finish()
}
Self::None => write!(f, "None"),
Self::RightClickSlide(arg0, arg1) => f
.debug_tuple("RightClickSlide")
.field(arg0)
.field(arg1)
.finish(),
Self::AssignObsScene(arg0) => {
f.debug_tuple("ObsSceneAssign").field(arg0).finish()
}
Self::UpdateObsScenes(arg0) => {
f.debug_tuple("UpdateObsScenes").field(arg0).finish()
}
Self::AddObsClient(_) => write!(f, "AddObsClient"),
Self::AssignSlideAction(action) => f
.debug_tuple("AssignSlideAction")
.field(action)
.finish(),
}
}
}
#[allow(clippy::enum_variant_names)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum MenuAction {
ObsSceneAssign(usize),
ObsStartStream,
ObsStopStream,
ObsStartRecord,
ObsStopRecord,
// ObsStartRecord,
// ObsStopRecord,
}
impl menu::Action for MenuAction {
@ -189,8 +129,8 @@ impl menu::Action for MenuAction {
action: ObsAction::StopStream,
},
),
Self::ObsStartRecord => todo!(),
Self::ObsStopRecord => todo!(),
// Self::ObsStartRecord => todo!(),
// Self::ObsStopRecord => todo!(),
}
}
}
@ -642,9 +582,9 @@ impl Presenter {
Message::HoveredSlide(slide) => {
self.hovered_slide = slide;
}
Message::StartAudio => {
return Action::Task(self.start_audio());
}
// Message::StartAudio => {
// return Action::Task(self.start_audio());
// }
Message::EndAudio => {
self.sink.0.stop();
}
@ -1008,13 +948,13 @@ impl Presenter {
}
}
#[allow(clippy::unused_async)]
async fn obs_scene_switch(client: Arc<Client>, scene: Scene) {
match client.scenes().set_current_program_scene(&scene.id).await {
Ok(()) => debug!("Set scene to: {:?}", scene),
Err(e) => error!(?e),
}
}
// #[allow(clippy::unused_async)]
// async fn obs_scene_switch(client: Arc<Client>, scene: Scene) {
// match client.scenes().set_current_program_scene(&scene.id).await {
// Ok(()) => debug!("Set scene to: {:?}", scene),
// Err(e) => error!(?e),
// }
// }
// This needs to be async so that rodio's audio will work
#[allow(clippy::unused_async)]
@ -1033,17 +973,6 @@ async fn start_audio(sink: Arc<Sink>, audio: PathBuf) {
debug!(empty, paused, "Finished running");
}
fn scale_font(font_size: f32, width: f32) -> f32 {
let scale_factor = (REFERENCE_WIDTH / width).sqrt();
// debug!(scale_factor);
if font_size > 0.0 {
font_size / scale_factor
} else {
50.0
}
}
pub(crate) fn slide_view<'a>(
slide: &'a Slide,
video: Option<&'a Video>,

View file

@ -344,7 +344,7 @@ struct State {
hovered: bool,
left_pressed_position: Option<Point>,
is_dragging: bool,
cached_bounds: Rectangle,
_cached_bounds: Rectangle,
}
impl State {

View file

@ -13,13 +13,13 @@ use tracing::debug;
#[derive(Debug, Default)]
struct State {
cache: canvas::Cache,
_cache: canvas::Cache,
}
#[derive(Debug, Default)]
pub struct SlideEditor {
state: State,
font: Font,
_state: State,
_font: Font,
program: EditorProgram,
}
@ -35,11 +35,11 @@ pub enum Message {
}
pub struct Text {
text: String,
_text: String,
}
pub struct Image {
source: PathBuf,
_source: PathBuf,
}
pub enum SlideWidget {
@ -55,7 +55,7 @@ pub enum SlideError {
#[derive(Debug, Default)]
struct EditorProgram {
mouse_button_pressed: Option<cosmic::iced::mouse::Button>,
_mouse_button_pressed: Option<cosmic::iced::mouse::Button>,
}
impl SlideEditor {

View file

@ -85,7 +85,7 @@ pub struct SongEditor {
video: Option<Video>,
ccli: String,
song_slides: Option<Vec<Slide>>,
slide_state: SlideEditor,
_slide_state: SlideEditor,
stroke_sizes: [String; 16],
shadow_sizes: [String; 16],
shadow_offset_sizes: [String; 21],
@ -253,7 +253,7 @@ impl SongEditor {
background: None,
video: None,
ccli: String::new(),
slide_state: SlideEditor::default(),
_slide_state: SlideEditor::default(),
song_slides: None,
stroke_sizes: [
"0".to_string(),
@ -641,7 +641,7 @@ impl SongEditor {
let verse_name = song
.verse_name_from_str(
verse_name,
&verse_name,
old_verse_name,
);

View file

@ -8,7 +8,7 @@ use cosmic::iced_wgpu::Primitive;
use cosmic::iced_wgpu::primitive::Renderer as PrimitiveRenderer;
pub struct SlideText {
text: String,
_text: String,
font_size: f32,
}
@ -16,7 +16,7 @@ impl SlideText {
pub fn new(text: impl AsRef<str>) -> Self {
let text = text.as_ref();
Self {
text: text.to_string(),
_text: text.to_string(),
font_size: 50.0,
}
}
@ -88,8 +88,8 @@ where
#[derive(Debug, Clone)]
pub(crate) struct TextPrimitive {
text_id: u64,
size: (u32, u32),
_text_id: u64,
_size: (u32, u32),
}
impl TextPrimitive {