Skip to content

Commit

Permalink
Select sources if sources are empty
Browse files Browse the repository at this point in the history
  • Loading branch information
quambene committed Mar 9, 2024
1 parent 96ca242 commit 318b981
Show file tree
Hide file tree
Showing 6 changed files with 217 additions and 192 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
### Unreleased

- added
- Select sources for `bogrep configure` if no sources are configured
- changed
- Fix removing ignored urls
- removed
Expand All @@ -15,10 +16,10 @@
### v0.8.0

- added
- Select sources for `bogrep import` if no sources are configured
- Implement running in dry mode
- Select sources from user input
- changed
- Select sources for `bogrep import` if no sources are configured
- Update rust toolchain to 1.76
- Fix `settings.json` for `bogrep config --ignore` and `bogrep config --underlying` if run
multiple times
Expand Down
3 changes: 3 additions & 0 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ pub enum Subcommands {
/// Describes the arguments for the `config` subcommand.
#[derive(ClapArgs, Debug)]
pub struct ConfigArgs {
/// Run command in dry mode.
#[arg(short = 'n', long = "dry-run")]
pub dry_run: bool,
#[command(flatten)]
pub set_source: SetSource,
#[command(flatten)]
Expand Down
195 changes: 191 additions & 4 deletions src/cmd/configure.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,43 @@
use crate::{
bookmark_reader::SourceReader, bookmarks::RawSource, cache::CacheMode, json, utils, Config,
ConfigArgs, Settings,
bookmark_reader::{SourceOs, SourceReader},
bookmarks::RawSource,
cache::CacheMode,
errors::BogrepError,
json, utils, Config, ConfigArgs, Settings,
};
use anyhow::Context;
use anyhow::{anyhow, Context};
use log::{debug, warn};
use std::{fs, io::Write};
use std::{
collections::HashSet,
fs,
io::{self, Write},
path::Path,
};

/// Configure the source files to import the bookmarks, the cache mode, or the
/// ignoure urls .
pub fn configure(config: Config, args: ConfigArgs) -> Result<(), anyhow::Error> {
debug!("{args:?}");

if args.dry_run {
println!("Running in dry mode ...")
}

let mut config = config;
let home_dir = dirs::home_dir().ok_or(anyhow!("Missing home dir"))?;

if config.settings.sources.is_empty() {
if let Some(source_os) = utils::get_supported_os() {
configure_sources(&mut config, &home_dir, &source_os)?;

if !args.dry_run {
let mut settings_file = utils::open_and_truncate_file(&config.settings_path)?;
let settings_json = json::serialize(config.settings.clone())?;
settings_file.write_all(&settings_json)?;
}
}
}

let cache_mode = args.set_cache_mode.cache_mode;
let source_path = args
.set_source
Expand Down Expand Up @@ -72,11 +99,171 @@ fn configure_settings(
Ok(())
}

/// Configure sources if no sources are configured.
pub fn configure_sources(
config: &mut Config,
home_dir: &Path,
source_os: &SourceOs,
) -> Result<(), anyhow::Error> {
let sources = SourceReader::select_sources(home_dir, source_os)?;
let indexed_sources = sources
.iter()
.enumerate()
.map(|(i, _)| i + 1)
.collect::<Vec<_>>();

println!("Found sources:");
for (index, source) in sources.iter().enumerate() {
println!("{}: {}", index + 1, source.path.display());
}

println!("Select sources: yes (y), no (n), or specify numbers separated by whitespaces");

let selected_indices = loop {
let mut input = String::new();
io::stdin().read_line(&mut input)?;
let input = input.trim().to_lowercase();
match select_sources_from_input(&input, &indexed_sources) {
Ok(selected_sources) => {
break selected_sources;
}
Err(_) => {
println!("Invalid input. Please try again");
continue;
}
}
};

if selected_indices.is_empty() {
println!("No sources selected. Aborting ...");
} else {
println!(
"Selected sources: {}",
selected_indices
.iter()
.map(|num| num.to_string())
.collect::<Vec<String>>()
.join(", ")
);
}

let selected_sources = selected_indices
.iter()
.filter_map(|&i| sources.get(i - 1))
.collect::<Vec<_>>();

for source in selected_sources {
config.settings.sources.push(source.to_owned());
}

Ok(())
}

fn select_sources_from_input(
input: &str,
indexed_sources: &[usize],
) -> Result<Vec<usize>, BogrepError> {
let choices: Vec<&str> = input.split_whitespace().collect();

if choices.len() == 1 {
match choices[0] {
"y" | "yes" => Ok(indexed_sources.to_vec()),
"n" | "no" => Ok(vec![]),
num => {
let num = num
.parse::<usize>()
.map_err(|_| BogrepError::InvalidInput)?;

if indexed_sources.contains(&num) {
Ok(vec![num])
} else {
Err(BogrepError::InvalidInput)
}
}
}
} else {
let nums: Result<Vec<usize>, _> = choices.iter().map(|s| s.parse::<usize>()).collect();
if let Ok(nums) = nums {
// Remove duplicates
let mut nums = nums
.into_iter()
.collect::<HashSet<_>>()
.into_iter()
.collect::<Vec<_>>();
nums.sort();

if nums.iter().all(|num| indexed_sources.contains(num)) {
Ok(nums)
} else {
Err(BogrepError::InvalidInput)
}
} else {
Err(BogrepError::InvalidInput)
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use std::{io::Cursor, path::PathBuf};

#[test]
fn test_select_sources_from_input() {
let indexed_sources = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

let selected_sources = select_sources_from_input("y", &indexed_sources).unwrap();
assert_eq!(selected_sources, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);

let selected_sources = select_sources_from_input("yes", &indexed_sources).unwrap();
assert_eq!(selected_sources, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);

let selected_sources = select_sources_from_input("n", &indexed_sources).unwrap();
assert_eq!(selected_sources, vec![] as Vec<usize>);

let selected_sources = select_sources_from_input("no", &indexed_sources).unwrap();
assert_eq!(selected_sources, vec![] as Vec<usize>);

let selected_sources = select_sources_from_input("1", &indexed_sources).unwrap();
assert_eq!(selected_sources, vec![1]);

let selected_sources = select_sources_from_input("1 5 10", &indexed_sources).unwrap();
assert_eq!(selected_sources, vec![1, 5, 10]);

let selected_sources = select_sources_from_input("1 1", &indexed_sources).unwrap();
assert_eq!(selected_sources, vec![1]);

let selected_sources = select_sources_from_input("1 5 1 10", &indexed_sources).unwrap();
assert_eq!(selected_sources, vec![1, 5, 10]);

let selected_sources = select_sources_from_input("1 5 1 10 0", &indexed_sources);
assert!(selected_sources.is_err());

let selected_sources = select_sources_from_input("x", &indexed_sources);
assert!(selected_sources.is_err());

let selected_sources = select_sources_from_input("x ", &indexed_sources);
assert!(selected_sources.is_err());

let selected_sources = select_sources_from_input(" x", &indexed_sources);
assert!(selected_sources.is_err());

let selected_sources = select_sources_from_input("xx", &indexed_sources);
assert!(selected_sources.is_err());

let selected_sources = select_sources_from_input("x x", &indexed_sources);
assert!(selected_sources.is_err());

let selected_sources = select_sources_from_input("0", &indexed_sources);
assert!(selected_sources.is_err());

let selected_sources = select_sources_from_input("11", &indexed_sources);
assert!(selected_sources.is_err());

let selected_sources = select_sources_from_input("1 5 11", &indexed_sources);
assert!(selected_sources.is_err());
}

#[test]
fn test_configure_source() {
let mut cursor = Cursor::new(Vec::new());
Expand Down
Loading

0 comments on commit 318b981

Please sign in to comment.