about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-08-26 20:20:15 +0200
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-08-26 20:20:15 +0200
commit6f0b086f7830f27a7d938bc03db0c73295d745e2 (patch)
treeb1b1a7229a9573ed20079452de7919364619d807
parentchore(version): v1.8.0 (diff)
downloadyt-6f0b086f7830f27a7d938bc03db0c73295d745e2.zip
feat(yt/commands/subs): Implement disabling subscriptions
This allows for a softer version of `unsubscribe`, as the subscription
can be enabled again.
-rw-r--r--crates/yt/src/commands/subscriptions/implm.rs42
-rw-r--r--crates/yt/src/commands/subscriptions/mod.rs26
-rw-r--r--crates/yt/src/commands/update/implm/mod.rs2
-rw-r--r--crates/yt/src/storage/db/get/subscription.rs16
-rw-r--r--crates/yt/src/storage/db/insert/subscription.rs33
-rw-r--r--crates/yt/src/storage/db/subscription.rs6
-rw-r--r--crates/yt/src/storage/migrate/mod.rs14
-rw-r--r--crates/yt/src/storage/migrate/sql/6_Five_to_Six.sql12
8 files changed, 140 insertions, 11 deletions
diff --git a/crates/yt/src/commands/subscriptions/implm.rs b/crates/yt/src/commands/subscriptions/implm.rs
index 31b714e..1e2e545 100644
--- a/crates/yt/src/commands/subscriptions/implm.rs
+++ b/crates/yt/src/commands/subscriptions/implm.rs
@@ -12,7 +12,7 @@ use std::str::FromStr;
 
 use crate::{
     app::App,
-    commands::subscriptions::SubscriptionCommand,
+    commands::subscriptions::{SubscriptionCommand, SubscriptionStatus},
     storage::db::{
         insert::{Operations, subscription::Operation},
         subscription::{Subscription, Subscriptions, check_url},
@@ -55,11 +55,41 @@ impl SubscriptionCommand {
                     .await
                     .with_context(|| format!("Failed to unsubscribe from {name:?}"))?;
             }
-            SubscriptionCommand::List {} => {
+            SubscriptionCommand::SetStatus { name, new_status } => {
+                let mut present_subscriptions = Subscriptions::get(app).await?;
+
+                let mut ops = Operations::new("Subscribe: Set Status");
+                if let Some(subscription) = present_subscriptions.0.remove(&name) {
+                    subscription.set_is_active(
+                        match new_status {
+                            SubscriptionStatus::Active => true,
+                            SubscriptionStatus::Inactive => false,
+                        },
+                        &mut ops,
+                    );
+                } else {
+                    bail!("Couldn't find subscription: '{}'", &name);
+                }
+                ops.commit(app)
+                    .await
+                    .with_context(|| format!("Failed to change status of {name:?}"))?;
+            }
+            SubscriptionCommand::List { active } => {
                 let all_subs = Subscriptions::get(app).await?;
 
+                let all_subs = if active {
+                    all_subs.remove_inactive()
+                } else {
+                    all_subs
+                };
+
                 for (key, val) in all_subs.0 {
-                    println!("{}: '{}'", key, val.url);
+                    println!(
+                        "{}: '{}' ({})",
+                        key,
+                        val.url,
+                        if val.is_active { "active" } else { "inactive" }
+                    );
                 }
             }
             SubscriptionCommand::Export {} => {
@@ -245,7 +275,11 @@ async fn actual_subscribe(
         );
     }
 
-    let sub = Subscription { name, url };
+    let sub = Subscription {
+        name,
+        url,
+        is_active: true,
+    };
 
     sub.add(ops);
 
diff --git a/crates/yt/src/commands/subscriptions/mod.rs b/crates/yt/src/commands/subscriptions/mod.rs
index edd41c6..6a16a9a 100644
--- a/crates/yt/src/commands/subscriptions/mod.rs
+++ b/crates/yt/src/commands/subscriptions/mod.rs
@@ -10,7 +10,7 @@
 
 use std::path::PathBuf;
 
-use clap::Subcommand;
+use clap::{Subcommand, ValueEnum};
 use clap_complete::ArgValueCompleter;
 use url::Url;
 
@@ -41,6 +41,18 @@ pub(super) enum SubscriptionCommand {
         name: String,
     },
 
+    /// Change the status of an subscription.
+    ///
+    /// An active subscription will be updated in `yt update`, while an inactive one will not.
+    SetStatus {
+        /// The human readable name of the subscription
+        #[arg(add = ArgValueCompleter::new(complete_subscription))]
+        name: String,
+
+        /// What should this subscription be considered now?
+        new_status: SubscriptionStatus,
+    },
+
     /// Import a bunch of URLs as subscriptions.
     Import {
         /// The file containing the URLs. Will use Stdin otherwise.
@@ -58,5 +70,15 @@ pub(super) enum SubscriptionCommand {
     Export {},
 
     /// List all subscriptions
-    List {},
+    List {
+        /// Only show active subscriptions
+        #[arg(short, long, default_value_t = false)]
+        active: bool,
+    },
+}
+
+#[derive(ValueEnum, Debug, Clone, Copy)]
+pub(in crate::commands) enum SubscriptionStatus {
+    Active,
+    Inactive,
 }
diff --git a/crates/yt/src/commands/update/implm/mod.rs b/crates/yt/src/commands/update/implm/mod.rs
index 53c7415..10626ac 100644
--- a/crates/yt/src/commands/update/implm/mod.rs
+++ b/crates/yt/src/commands/update/implm/mod.rs
@@ -28,7 +28,7 @@ impl UpdateCommand {
             subscriptions: subscription_names_to_update,
         } = self;
 
-        let mut all_subs = Subscriptions::get(app).await?;
+        let mut all_subs = Subscriptions::get(app).await?.remove_inactive();
 
         let max_backlog = max_backlog.unwrap_or(app.config.update.max_backlog);
 
diff --git a/crates/yt/src/storage/db/get/subscription.rs b/crates/yt/src/storage/db/get/subscription.rs
index 16a6e8b..1d0b660 100644
--- a/crates/yt/src/storage/db/get/subscription.rs
+++ b/crates/yt/src/storage/db/get/subscription.rs
@@ -39,6 +39,13 @@ impl Subscriptions {
                     Subscription::new(
                         sub.name,
                         Url::parse(&sub.url).expect("It was an URL, when we inserted it."),
+                        if sub.is_active == 1 {
+                            true
+                        } else if sub.is_active == 0 {
+                            false
+                        } else {
+                            unreachable!("These are the only two options")
+                        },
                     ),
                 )
             })
@@ -46,4 +53,13 @@ impl Subscriptions {
 
         Ok(Subscriptions(subscriptions))
     }
+
+    pub(crate) fn remove_inactive(self) -> Self {
+        Self(
+            self.0
+                .into_iter()
+                .filter(|(_, sub)| sub.is_active)
+                .collect(),
+        )
+    }
 }
diff --git a/crates/yt/src/storage/db/insert/subscription.rs b/crates/yt/src/storage/db/insert/subscription.rs
index d25a209..54409a9 100644
--- a/crates/yt/src/storage/db/insert/subscription.rs
+++ b/crates/yt/src/storage/db/insert/subscription.rs
@@ -21,6 +21,10 @@ use sqlx::query;
 pub(crate) enum Operation {
     Add(Subscription),
     Remove(Subscription),
+    SetIsActive {
+        target: Subscription,
+        is_active: bool,
+    },
 }
 
 impl Committable for Operation {
@@ -72,6 +76,26 @@ impl Committable for Operation {
 
                 Ok(())
             }
+            Operation::SetIsActive { target, is_active } => {
+                query!(
+                    "
+                    UPDATE subscriptions
+                    SET is_active = ?
+                    WHERE name = ?;
+                    ",
+                    is_active,
+                    target.name
+                )
+                .execute(txn)
+                .await?;
+
+                println!(
+                    "Marked '{}' as '{}'",
+                    target.name,
+                    if is_active { "active" } else { "inactive" }
+                );
+                Ok(())
+            }
         }
     }
 }
@@ -84,6 +108,15 @@ impl Subscription {
     pub(crate) fn remove(self, ops: &mut Operations<Operation>) {
         ops.push(Operation::Remove(self));
     }
+
+    pub(crate) fn set_is_active(self, is_active: bool, ops: &mut Operations<Operation>) {
+        if self.is_active != is_active {
+            ops.push(Operation::SetIsActive {
+                target: self,
+                is_active,
+            });
+        }
+    }
 }
 
 impl Subscriptions {
diff --git a/crates/yt/src/storage/db/subscription.rs b/crates/yt/src/storage/db/subscription.rs
index c111b52..403938e 100644
--- a/crates/yt/src/storage/db/subscription.rs
+++ b/crates/yt/src/storage/db/subscription.rs
@@ -23,12 +23,14 @@ pub(crate) struct Subscription {
 
     /// The URL this subscription subscribes to
     pub(crate) url: Url,
+
+    pub(crate) is_active: bool,
 }
 
 impl Subscription {
     #[must_use]
-    pub(crate) fn new(name: String, url: Url) -> Self {
-        Self { name, url }
+    pub(crate) fn new(name: String, url: Url, is_active: bool) -> Self {
+        Self { name, url, is_active }
     }
 }
 
diff --git a/crates/yt/src/storage/migrate/mod.rs b/crates/yt/src/storage/migrate/mod.rs
index 418c893..c5187ee 100644
--- a/crates/yt/src/storage/migrate/mod.rs
+++ b/crates/yt/src/storage/migrate/mod.rs
@@ -97,8 +97,11 @@ pub(crate) enum DbVersion {
 
     /// Introduced: 2025-07-20.
     Five,
+
+    /// Introduced: 2025-08-26.
+    Six,
 }
-const CURRENT_VERSION: DbVersion = DbVersion::Five;
+const CURRENT_VERSION: DbVersion = DbVersion::Six;
 
 async fn add_error_context(
     function: impl Future<Output = Result<()>>,
@@ -151,6 +154,7 @@ impl DbVersion {
             DbVersion::Three => 3,
             DbVersion::Four => 4,
             DbVersion::Five => 5,
+            DbVersion::Six => 6,
 
             DbVersion::Empty => unreachable!("A empty version does not have an associated integer"),
         }
@@ -164,6 +168,7 @@ impl DbVersion {
             (3, "yt") => Ok(DbVersion::Three),
             (4, "yt") => Ok(DbVersion::Four),
             (5, "yt") => Ok(DbVersion::Five),
+            (6, "yt") => Ok(DbVersion::Six),
 
             (0, other) => bail!("Db version is Zero, but got unknown namespace: '{other}'"),
             (1, other) => bail!("Db version is One, but got unknown namespace: '{other}'"),
@@ -171,6 +176,7 @@ impl DbVersion {
             (3, other) => bail!("Db version is Three, but got unknown namespace: '{other}'"),
             (4, other) => bail!("Db version is Four, but got unknown namespace: '{other}'"),
             (5, other) => bail!("Db version is Five, but got unknown namespace: '{other}'"),
+            (6, other) => bail!("Db version is Six, but got unknown namespace: '{other}'"),
 
             (other, "yt") => bail!("Got unkown version for 'yt' namespace: {other}"),
             (num, nasp) => bail!("Got unkown version number ({num}) and namespace ('{nasp}')"),
@@ -208,8 +214,12 @@ impl DbVersion {
                 make_upgrade! {app, Self::Four, Self::Five, "./sql/5_Four_to_Five.sql"}
             }
 
-            // This is the current_version
             Self::Five => {
+                make_upgrade! {app, Self::Five, Self::Six, "./sql/6_Five_to_Six.sql"}
+            }
+
+            // This is the current_version
+            Self::Six => {
                 assert_eq!(self, CURRENT_VERSION);
                 assert_eq!(self, get_version(app).await?);
                 Ok(())
diff --git a/crates/yt/src/storage/migrate/sql/6_Five_to_Six.sql b/crates/yt/src/storage/migrate/sql/6_Five_to_Six.sql
new file mode 100644
index 0000000..6a2cbcc
--- /dev/null
+++ b/crates/yt/src/storage/migrate/sql/6_Five_to_Six.sql
@@ -0,0 +1,12 @@
+-- yt - A fully featured command line YouTube client
+--
+-- Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+-- SPDX-License-Identifier: GPL-3.0-or-later
+--
+-- This file is part of Yt.
+--
+-- You should have received a copy of the License along with this program.
+-- If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+ALTER TABLE subscriptions
+ADD COLUMN is_active INTEGER NOT NULL DEFAULT 1 CHECK (is_active IN (0, 1));