// nixos-config - My current NixOS configuration // // Copyright (C) 2025 Benedikt Peetz // SPDX-License-Identifier: GPL-3.0-or-later // // This file is part of my nixos-config. // // You should have received a copy of the License along with this program. // If not, see . use std::{ collections::{HashMap, HashSet}, fs, str::FromStr, }; use anyhow::{Context, Result}; use log::info; use crate::{browser::open_in_browser, cli::InputCommand, state::State}; use super::{Input, Tag}; /// # Errors /// When command handling fails. /// /// # Panics /// When internal assertions fail. #[allow(clippy::too_many_lines)] pub fn handle(command: InputCommand, state: &mut State) -> Result<()> { match command { InputCommand::Add { inputs } => { for input in inputs { input.commit().with_context(|| { format!("Failed to add input ('{input}') to the input storage.") })?; } } InputCommand::Remove { inputs } => { for input in inputs { input.remove().with_context(|| { format!("Failed to remove input ('{input}') from the input storage.") })?; } } InputCommand::File { file, tags } => { let file = fs::read_to_string(&file) .with_context(|| format!("Failed to read input file '{}'", file.display()))?; let mut tag_set = HashSet::with_capacity(tags.len()); for tag in tags { tag_set.insert(tag); } for line in file.lines().map(str::trim) { if line.is_empty() { continue; } let mut input = Input::from_str(line)?; input.tags = input.tags.union(&tag_set).cloned().collect(); input.commit().with_context(|| { format!("Failed to add input ('{input}') to the input storage.") })?; } } InputCommand::Review { project } => { 'outer: for all in Input::all()?.chunks(100) { info!("Starting review for the first hundred URLs."); for input in all { info!("-> '{input}'"); open_in_browser(&project, state, Some(input.url.clone()))?; } { use std::io::{stdin, stdout, Write}; let mut s = String::new(); eprint!("Continue? (y/N) "); stdout().flush()?; stdin() .read_line(&mut s) .expect("Did not enter a correct string"); if let Some('\n') = s.chars().next_back() { s.pop(); } if let Some('\r') = s.chars().next_back() { s.pop(); } if s != "y" { break 'outer; } } } } InputCommand::List { tags } => { let mut tag_set = HashSet::with_capacity(tags.len()); for tag in tags { tag_set.insert(tag); } for url in Input::all()? .iter() .filter(|input| tag_set.is_subset(&input.tags)) { println!("{url}"); } } InputCommand::Tags {} => { let mut without_tags = 0; let mut tag_set: HashMap = HashMap::new(); for input in Input::all()? { if input.tags.is_empty() { without_tags += 1; } for tag in input.tags { if let Some(number) = tag_set.get_mut(&tag) { *number += 1; } else { tag_set.insert(tag, 1); } } } let mut tags: Vec<(Tag, u64)> = tag_set.into_iter().collect(); tags.sort_by_key(|(_, number)| *number); tags.reverse(); for (tag, number) in tags { println!("{tag} {number}"); } if without_tags != 0 { println!(); println!("Witohut tags: {without_tags}"); } } } Ok(()) }