0
0
mirror of https://github.com/TeFiLeDo/listload.git synced 2024-11-01 01:56:17 +01:00

target creation and selection

This commit is contained in:
Adrian Wannenmacher 2023-06-27 21:52:10 +02:00
parent 74cbd9d6f8
commit d10022fec8
Signed by: tfld
GPG Key ID: 19D986ECB1E492D5
5 changed files with 111 additions and 29 deletions

View File

@ -11,6 +11,7 @@ anyhow = "1.0.71"
clap = { version = "4.3.8", features = ["cargo", "derive", "env", "wrap_help"] }
directories = "5.0.1"
downloader = { version = "0.2.7", default-features = false, features = ["rustls-tls"] }
indoc = "2.0.1"
regex = "1.8.4"
serde = { version = "1.0.164", features = ["derive"] }
serde_json = "1.0.99"

View File

@ -1,11 +1,13 @@
use std::sync::OnceLock;
use std::{path::PathBuf, sync::OnceLock};
use anyhow::{ensure, Context};
use clap::{Parser, Subcommand};
use config::Config;
use directories::{ProjectDirs, UserDirs};
use persistent_state::PersistentState;
use target::Target;
use target_list::TargetList;
use url::Url;
mod config;
mod persistent_state;
@ -50,13 +52,12 @@ fn main() -> anyhow::Result<()> {
CMD::PersistentState => {
println!("{persistent}");
}
CMD::List { cmd } => 'list: {
if let ListCommand::Create {
CMD::List { cmd } => match cmd {
ListCommand::Create {
name,
keep_current_active,
keep_current_selected: keep_current_active,
comment,
} = cmd
{
} => {
if TargetList::exists(&name) {
eprintln!("list already exists");
} else {
@ -67,30 +68,42 @@ fn main() -> anyhow::Result<()> {
if !keep_current_active {
persistent.set_list(&name);
}
break 'list;
} else if let ListCommand::Select { name } = cmd {
}
ListCommand::Select { name } => {
if name == "none" {
persistent.clear_list();
} else if !TargetList::exists(&name) {
eprintln!("list doesn't exist");
} else {
} else if TargetList::exists(&name) {
persistent.set_list(&name);
} else {
eprintln!("list doesn't exist");
}
break 'list;
}
},
CMD::Target { cmd } => {
let list = persistent.list().context("no list selected")?;
let mut list = TargetList::load(&list).context("failed to load list")?;
match cmd {
ListCommand::Create {
name: _,
keep_current_active: _,
comment: _,
TargetCommand::Create {
file,
url,
comment,
keep_current_selected,
} => {
let target = Target::new(url, &file, comment.as_ref().map(|c| c.as_str()))
.context("invalid target")?;
list.add_target(target);
if !keep_current_selected {
persistent.set_target(list.len_targets() - 1);
}
}
| ListCommand::Select { name: _ } => {
panic!("late list command");
TargetCommand::Select { index } => {
if index < list.len_targets() {
persistent.set_target(index);
} else {
eprintln!("target doesn't exist");
}
}
}
@ -124,6 +137,11 @@ enum CMD {
#[clap(subcommand)]
cmd: ListCommand,
},
/// Individual target operations.
Target {
#[clap(subcommand)]
cmd: TargetCommand,
},
}
#[derive(Subcommand)]
@ -140,14 +158,16 @@ enum ListCommand {
///
/// Invalid examples: none, 14, _hi, hi_, h__i
name: String,
/// Don't activate the newly created list.
#[clap(long, short)]
keep_current_active: bool,
/// A comment to remember what a list is for.
/// A comment to remember what the list is meant to do.
#[clap(long, short)]
comment: Option<String>,
/// Don't select the newly created list.
#[clap(long, short)]
keep_current_selected: bool,
},
/// Select an existing list.
///
/// List selection is important for the `target` subcommand.
Select {
/// The name of the list.
///
@ -156,3 +176,26 @@ enum ListCommand {
name: String,
},
}
#[derive(Subcommand)]
enum TargetCommand {
/// Create a new target.
Create {
/// The local file name.
file: PathBuf,
/// A list of URLs the file is available at.
url: Vec<Url>,
/// A comment to remember why the target is in the list.
#[clap(long, short)]
comment: Option<String>,
/// Don't select the newly created target.
#[clap(long, short)]
keep_current_selected: bool,
},
/// Select an existing target.
/// Target selection is important for the `url` subcommand.
Select {
/// The index of the target.
index: usize,
},
}

View File

@ -6,6 +6,7 @@ use std::{
};
use anyhow::{bail, ensure, Context};
use indoc::writedoc;
use serde::{Deserialize, Serialize};
use crate::{target_list::TargetList, PROJ_DIRS};
@ -16,6 +17,7 @@ const PERSISTENT_FILE: &str = "persistent_state.json";
#[serde(deny_unknown_fields, default)]
pub struct PersistentState {
list: Option<String>,
target: Option<usize>,
}
impl PersistentState {
@ -25,10 +27,22 @@ impl PersistentState {
pub fn set_list(&mut self, list: &str) {
self.list = Some(list.to_string());
self.target = None;
}
pub fn clear_list(&mut self) {
self.list = None;
self.target = None;
}
pub fn target(&self) -> Option<usize> {
self.target
}
pub fn set_target(&mut self, index: usize) {
if self.list.is_some() {
self.target = Some(index);
}
}
}
@ -95,10 +109,15 @@ impl PersistentState {
impl Display for PersistentState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
writedoc!(
f,
"current list: {}",
self.list.as_ref().map(AsRef::as_ref).unwrap_or("none")
"
current list: {}
current target: {}",
self.list.as_ref().map(AsRef::as_ref).unwrap_or("none"),
self.target
.map(|t| t.to_string())
.unwrap_or("none".to_string())
)
}
}

View File

@ -23,6 +23,11 @@ impl Target {
"file must be file or nonexistent"
);
for url in &urls {
let scheme = url.scheme();
ensure!(scheme == "http" || scheme == "https", "url is not http(s)");
}
Ok(Self {
urls,
file: file.to_path_buf(),

View File

@ -1,6 +1,6 @@
use std::{
fs::{create_dir_all, File, OpenOptions},
io::{BufReader, BufWriter},
io::{BufReader, BufWriter, Seek, SeekFrom},
path::{Path, PathBuf},
};
@ -20,6 +20,14 @@ impl TargetList {
pub fn name(&self) -> &str {
&self.inner.name
}
pub fn add_target(&mut self, target: Target) {
self.inner.targets.push(target);
}
pub fn len_targets(&self) -> usize {
self.inner.targets.len()
}
}
impl TargetList {
@ -115,7 +123,13 @@ impl TargetList {
}
pub fn save(&mut self) -> anyhow::Result<()> {
self.file.set_len(0);
let _ = self
.file
.set_len(0)
.context("failed to clear target list file");
self.file
.seek(SeekFrom::Start(0))
.context("failed to rewind target list file")?;
serde_json::to_writer(BufWriter::new(&self.file), &self.inner)
.context("failed to save target list")
}