11use chrono:: Utc ;
22use manga_tui:: SearchTerm ;
3- use rusqlite:: { Connection , OptionalExtension , params} ;
3+ use rusqlite:: types:: ToSqlOutput ;
4+ use rusqlite:: { Connection , OptionalExtension , ToSql , params} ;
45use strum:: { Display , EnumIter } ;
56
67use super :: AppDirectories ;
@@ -13,6 +14,14 @@ pub enum MangaHistoryType {
1314 ReadingHistory ,
1415}
1516
17+ impl ToSql for MangaHistoryType {
18+ fn to_sql ( & self ) -> rusqlite:: Result < rusqlite:: types:: ToSqlOutput < ' _ > > {
19+ let as_string = self . to_string ( ) ;
20+
21+ Ok ( ToSqlOutput :: from ( as_string) )
22+ }
23+ }
24+
1625impl From < FeedTabs > for MangaHistoryType {
1726 fn from ( value : FeedTabs ) -> Self {
1827 match value {
@@ -77,6 +86,7 @@ pub struct MangaHistoryResponse {
7786 pub total_items : u32 ,
7887}
7988
89+ #[ derive( Debug ) ]
8090pub struct GetHistoryArgs {
8191 pub hist_type : MangaHistoryType ,
8292 pub page : u32 ,
@@ -107,7 +117,7 @@ pub struct SetChapterDownloaded<'a> {
107117
108118// First check if the chapters is already in the database, if not insert it, or else update and set
109119// its download status to true
110-
120+ # [ derive ( Debug ) ]
111121pub struct Database < ' a > {
112122 connection : & ' a Connection ,
113123}
@@ -193,6 +203,7 @@ impl<'a> Database<'a> {
193203 Ok ( ( ) )
194204 }
195205
206+ #[ inline]
196207 pub fn get_connection ( ) -> rusqlite:: Result < Connection > {
197208 if cfg ! ( test) { Connection :: open_in_memory ( ) } else { Connection :: open ( AppDirectories :: History . get_full_path ( ) ) }
198209 }
@@ -349,7 +360,7 @@ impl<'a> Database<'a> {
349360 }
350361
351362 /// Insert a manga in the reading history type or update the `last_read` field
352- fn update_or_insert_manga_most_recent_read ( & self , manga_id : & str ) -> rusqlite:: Result < ( ) > {
363+ fn update_or_insert_manga_in_most_recent_reading_history ( & self , manga_id : & str ) -> rusqlite:: Result < ( ) > {
353364 if !self . manga_is_reading ( manga_id) ? {
354365 self . insert_manga_in_reading_history ( manga_id) ?;
355366 Ok ( ( ) )
@@ -363,7 +374,7 @@ impl<'a> Database<'a> {
363374
364375 pub fn set_chapter_downloaded ( & self , chapter : SetChapterDownloaded < ' _ > ) -> rusqlite:: Result < ( ) > {
365376 if self . check_exists ( chapter. manga_id , Table :: Mangas ) ? {
366- self . update_or_insert_manga_most_recent_read ( chapter. manga_id ) ?;
377+ self . update_or_insert_manga_in_most_recent_reading_history ( chapter. manga_id ) ?;
367378
368379 if self . check_exists ( chapter. id , Table :: Chapters ) ? {
369380 self . connection
@@ -434,13 +445,7 @@ impl<'a> Database<'a> {
434445 number_page_bookmarked : None ,
435446 } ) ?;
436447
437- if !self . manga_is_reading ( data. id ) ? {
438- self . insert_manga_in_reading_history ( data. id ) ?;
439- } else {
440- let now = Utc :: now ( ) . naive_utc ( ) ;
441- self . connection
442- . execute ( "UPDATE mangas SET last_read = ?1 WHERE id = ?2" , params ! [ now. to_string( ) , data. id] ) ?;
443- }
448+ self . update_or_insert_manga_in_most_recent_reading_history ( data. id ) ?;
444449
445450 self . connection
446451 . execute ( "UPDATE chapters SET is_read = true WHERE id = ?1" , params ! [ data. chapter. id] ) ?;
@@ -466,8 +471,6 @@ impl<'a> Database<'a> {
466471
467472 self . connection
468473 . execute ( "INSERT INTO manga_history_union VALUES (?1, ?2)" , ( manga. id , history_type) ) ?;
469-
470- return Ok ( ( ) ) ;
471474 }
472475 Ok ( ( ) )
473476 }
@@ -608,6 +611,30 @@ impl<'a> Database<'a> {
608611 page : args. page ,
609612 } )
610613 }
614+
615+ pub fn remove_from_history ( & self , manga_id : & str ) -> rusqlite:: Result < ( ) > {
616+ self . connection
617+ . execute ( "DELETE FROM manga_history_union WHERE manga_history_union.manga_id = ?1" , params ! [ manga_id] ) ?;
618+
619+ Ok ( ( ) )
620+ }
621+
622+ pub fn remove_all_from_history ( & self , hist_type : MangaHistoryType , provider : MangaProviders ) -> rusqlite:: Result < ( ) > {
623+ let history_type_id = self . get_history_type ( hist_type) ?;
624+
625+ let get_ids_manga_to_delete_statement = r#"SELECT mangas.id from mangas
626+ INNER JOIN manga_history_union ON mangas.id = manga_history_union.manga_id
627+ WHERE manga_history_union.type_id = ?1 AND mangas.manga_provider = ?2
628+ "# ;
629+
630+ let delete_statement =
631+ format ! ( "DELETE FROM manga_history_union WHERE manga_history_union.manga_id IN ({get_ids_manga_to_delete_statement})" ) ;
632+
633+ self . connection
634+ . execute ( & delete_statement, params ! [ history_type_id, provider. to_string( ) ] ) ?;
635+
636+ Ok ( ( ) )
637+ }
611638}
612639
613640#[ derive( Default , Debug , Clone ) ]
@@ -666,6 +693,8 @@ impl<'a> RetrieveBookmark for Database<'a> {
666693#[ cfg( test) ]
667694mod test {
668695
696+ use std:: error:: Error ;
697+
669698 use pretty_assertions:: assert_eq;
670699 use rusqlite:: Result ;
671700 use strum:: IntoEnumIterator ;
@@ -1664,4 +1693,261 @@ mod test {
16641693
16651694 Ok ( ( ) )
16661695 }
1696+
1697+ #[ test]
1698+ fn it_deletes_a_manga_from_reading_history ( ) -> Result < ( ) , Box < dyn Error > > {
1699+ let connection = Database :: get_connection ( ) ?;
1700+ let database = Database :: new ( & connection) ;
1701+ database. setup ( ) ?;
1702+
1703+ let manga_id_mangadex = Uuid :: new_v4 ( ) . to_string ( ) ;
1704+ let manga_id_mangadex2 = Uuid :: new_v4 ( ) . to_string ( ) ;
1705+
1706+ database. create_manga_if_not_exists ( MangaInsert {
1707+ id : & manga_id_mangadex,
1708+ title : "of mangadex 1" ,
1709+ img_url : None ,
1710+ provider : MangaProviders :: Mangadex ,
1711+ } ) ?;
1712+
1713+ database. insert_manga_in_reading_history ( & manga_id_mangadex) ?;
1714+
1715+ database. create_manga_if_not_exists ( MangaInsert {
1716+ id : & manga_id_mangadex2,
1717+ title : "of mangadex 2" ,
1718+ img_url : None ,
1719+ provider : MangaProviders :: Mangadex ,
1720+ } ) ?;
1721+
1722+ database. insert_manga_in_reading_history ( & manga_id_mangadex2) ?;
1723+
1724+ let expected = database. get_history ( GetHistoryArgs {
1725+ hist_type : MangaHistoryType :: ReadingHistory ,
1726+ page : 1 ,
1727+ search : None ,
1728+ items_per_page : 10 ,
1729+ provider : MangaProviders :: Mangadex ,
1730+ } ) ?;
1731+
1732+ /* at this point 2 mangas must be stored in reading history */
1733+ assert_eq ! ( expected. mangas. len( ) , 2 ) ;
1734+
1735+ database. remove_from_history ( & manga_id_mangadex) ?;
1736+
1737+ let expected = database. get_history ( GetHistoryArgs {
1738+ hist_type : MangaHistoryType :: ReadingHistory ,
1739+ page : 1 ,
1740+ search : None ,
1741+ items_per_page : 10 ,
1742+ provider : MangaProviders :: Mangadex ,
1743+ } ) ?;
1744+
1745+ /* now only one should exist */
1746+ assert_eq ! ( expected. mangas. len( ) , 1 ) ;
1747+
1748+ Ok ( ( ) )
1749+ }
1750+
1751+ #[ test]
1752+ fn it_deletes_a_manga_from_plan_to_read ( ) -> Result < ( ) , Box < dyn Error > > {
1753+ let connection = Database :: get_connection ( ) ?;
1754+ let database = Database :: new ( & connection) ;
1755+ database. setup ( ) ?;
1756+
1757+ let manga_id_mangadex = Uuid :: new_v4 ( ) . to_string ( ) ;
1758+ let manga_id_mangadex2 = Uuid :: new_v4 ( ) . to_string ( ) ;
1759+
1760+ database. save_plan_to_read ( MangaPlanToReadSave {
1761+ id : & manga_id_mangadex,
1762+ title : "of mangadex 1" ,
1763+ img_url : None ,
1764+ provider : MangaProviders :: Mangadex ,
1765+ } ) ?;
1766+
1767+ database. insert_manga_in_reading_history ( & manga_id_mangadex) ?;
1768+
1769+ database. save_plan_to_read ( MangaPlanToReadSave {
1770+ id : & manga_id_mangadex2,
1771+ title : "of mangadex 2" ,
1772+ img_url : None ,
1773+ provider : MangaProviders :: Mangadex ,
1774+ } ) ?;
1775+
1776+ database. insert_manga_in_reading_history ( & manga_id_mangadex2) ?;
1777+
1778+ let expected = database. get_history ( GetHistoryArgs {
1779+ hist_type : MangaHistoryType :: PlanToRead ,
1780+ page : 1 ,
1781+ search : None ,
1782+ items_per_page : 10 ,
1783+ provider : MangaProviders :: Mangadex ,
1784+ } ) ?;
1785+
1786+ /* at this point 2 mangas must be stored in reading history */
1787+ assert_eq ! ( expected. mangas. len( ) , 2 ) ;
1788+
1789+ database. remove_from_history ( & manga_id_mangadex) ?;
1790+
1791+ let expected = database. get_history ( GetHistoryArgs {
1792+ hist_type : MangaHistoryType :: PlanToRead ,
1793+ page : 1 ,
1794+ search : None ,
1795+ items_per_page : 10 ,
1796+ provider : MangaProviders :: Mangadex ,
1797+ } ) ?;
1798+
1799+ /* now only one should exist */
1800+ assert_eq ! ( expected. mangas. len( ) , 1 ) ;
1801+
1802+ Ok ( ( ) )
1803+ }
1804+
1805+ #[ test]
1806+ fn it_delets_all_mangas_from_history ( ) -> Result < ( ) , Box < dyn Error > > {
1807+ let connection = Database :: get_connection ( ) ?;
1808+ let database = Database :: new ( & connection) ;
1809+ database. setup ( ) ?;
1810+
1811+ let manga_id_mangadex = Uuid :: new_v4 ( ) . to_string ( ) ;
1812+ let manga_id_mangadex2 = Uuid :: new_v4 ( ) . to_string ( ) ;
1813+ let manga_id_mangadex3 = Uuid :: new_v4 ( ) . to_string ( ) ;
1814+
1815+ let manga_id_plan_to_read = Uuid :: new_v4 ( ) . to_string ( ) ;
1816+ let manga_id_plan_to_read2 = Uuid :: new_v4 ( ) . to_string ( ) ;
1817+
1818+ let manga_id_weeb_central = Uuid :: new_v4 ( ) . to_string ( ) ;
1819+ let manga_id_weeb_centra2 = Uuid :: new_v4 ( ) . to_string ( ) ;
1820+
1821+ database. create_manga_if_not_exists ( MangaInsert {
1822+ id : & manga_id_mangadex,
1823+ title : "of mangadex 1" ,
1824+ img_url : None ,
1825+ provider : MangaProviders :: Mangadex ,
1826+ } ) ?;
1827+
1828+ database. insert_manga_in_reading_history ( & manga_id_mangadex) ?;
1829+
1830+ database. create_manga_if_not_exists ( MangaInsert {
1831+ id : & manga_id_mangadex2,
1832+ title : "of mangadex 2" ,
1833+ img_url : None ,
1834+ provider : MangaProviders :: Mangadex ,
1835+ } ) ?;
1836+
1837+ database. insert_manga_in_reading_history ( & manga_id_mangadex2) ?;
1838+
1839+ database. create_manga_if_not_exists ( MangaInsert {
1840+ id : & manga_id_mangadex3,
1841+ title : "of mangadex 3" ,
1842+ img_url : None ,
1843+ provider : MangaProviders :: Mangadex ,
1844+ } ) ?;
1845+
1846+ database. insert_manga_in_reading_history ( & manga_id_mangadex3) ?;
1847+
1848+ database. create_manga_if_not_exists ( MangaInsert {
1849+ id : & manga_id_weeb_central,
1850+ title : "of weebcentral" ,
1851+ img_url : None ,
1852+ provider : MangaProviders :: Weebcentral ,
1853+ } ) ?;
1854+
1855+ database. insert_manga_in_reading_history ( & manga_id_weeb_central) ?;
1856+
1857+ database. create_manga_if_not_exists ( MangaInsert {
1858+ id : & manga_id_weeb_centra2,
1859+ title : "of weebcentral #2" ,
1860+ img_url : None ,
1861+ provider : MangaProviders :: Weebcentral ,
1862+ } ) ?;
1863+
1864+ database. insert_manga_in_reading_history ( & manga_id_weeb_centra2) ?;
1865+
1866+ let expected = database. get_history ( GetHistoryArgs {
1867+ hist_type : MangaHistoryType :: ReadingHistory ,
1868+ page : 1 ,
1869+ search : None ,
1870+ items_per_page : 10 ,
1871+ provider : MangaProviders :: Mangadex ,
1872+ } ) ?;
1873+
1874+ /* at this point 3 mangas must be stored in reading history */
1875+ assert_eq ! ( expected. mangas. len( ) , 3 ) ;
1876+
1877+ database. create_manga_if_not_exists ( MangaInsert {
1878+ id : & manga_id_plan_to_read,
1879+ title : "of mangadex 1" ,
1880+ img_url : None ,
1881+ provider : MangaProviders :: Mangadex ,
1882+ } ) ?;
1883+
1884+ database. save_plan_to_read ( MangaPlanToReadSave {
1885+ id : & manga_id_plan_to_read,
1886+ title : "of mangadex 1 plan to read" ,
1887+ img_url : None ,
1888+ provider : MangaProviders :: Mangadex ,
1889+ } ) ?;
1890+
1891+ database. create_manga_if_not_exists ( MangaInsert {
1892+ id : & manga_id_plan_to_read2,
1893+ title : "of mangadex 1" ,
1894+ img_url : None ,
1895+ provider : MangaProviders :: Mangadex ,
1896+ } ) ?;
1897+
1898+ database. save_plan_to_read ( MangaPlanToReadSave {
1899+ id : & manga_id_plan_to_read2,
1900+ title : "of mangadex 2 plan to read" ,
1901+ img_url : None ,
1902+ provider : MangaProviders :: Mangadex ,
1903+ } ) ?;
1904+
1905+ let expected = database. get_history ( GetHistoryArgs {
1906+ hist_type : MangaHistoryType :: PlanToRead ,
1907+ page : 1 ,
1908+ search : None ,
1909+ items_per_page : 10 ,
1910+ provider : MangaProviders :: Mangadex ,
1911+ } ) ?;
1912+
1913+ /* at this point 2 mangas in plant to read should exist */
1914+ assert_eq ! ( expected. mangas. len( ) , 2 ) ;
1915+
1916+ database. remove_all_from_history ( MangaHistoryType :: ReadingHistory , MangaProviders :: Mangadex ) ?;
1917+
1918+ let expected = database. get_history ( GetHistoryArgs {
1919+ hist_type : MangaHistoryType :: ReadingHistory ,
1920+ page : 1 ,
1921+ search : None ,
1922+ items_per_page : 10 ,
1923+ provider : MangaProviders :: Mangadex ,
1924+ } ) ?;
1925+
1926+ /* now none from mangadex should exist */
1927+ assert_eq ! ( expected. mangas. len( ) , 0 ) ;
1928+
1929+ let expected = database. get_history ( GetHistoryArgs {
1930+ hist_type : MangaHistoryType :: ReadingHistory ,
1931+ page : 1 ,
1932+ search : None ,
1933+ items_per_page : 10 ,
1934+ provider : MangaProviders :: Weebcentral ,
1935+ } ) ?;
1936+
1937+ /* weebcentral reading history should remain untouched */
1938+ assert_eq ! ( expected. mangas. len( ) , 2 ) ;
1939+
1940+ let expected = database. get_history ( GetHistoryArgs {
1941+ hist_type : MangaHistoryType :: PlanToRead ,
1942+ page : 1 ,
1943+ search : None ,
1944+ items_per_page : 10 ,
1945+ provider : MangaProviders :: Mangadex ,
1946+ } ) ?;
1947+
1948+ /* the plan to read history should remain untouched */
1949+ assert_eq ! ( expected. mangas. len( ) , 2 ) ;
1950+
1951+ Ok ( ( ) )
1952+ }
16671953}
0 commit comments