Skip to main content

sift_queue/cli/commands/
add.rs

1use crate::queue::{parse_priority_value, Queue, Source};
2use crate::AddArgs;
3use anyhow::Result;
4use std::io::Read;
5use std::path::PathBuf;
6
7/// Execute the `sq add` command.
8pub fn execute(args: &AddArgs, queue_path: PathBuf) -> Result<i32> {
9    let queue = Queue::new(queue_path);
10
11    let mut sources: Vec<Source> = Vec::new();
12
13    for path in &args.diff {
14        sources.push(Source {
15            type_: "diff".to_string(),
16            path: Some(path.clone()),
17            content: None,
18        });
19    }
20
21    for path in &args.file {
22        sources.push(Source {
23            type_: "file".to_string(),
24            path: Some(path.clone()),
25            content: None,
26        });
27    }
28
29    for text in &args.text {
30        sources.push(Source {
31            type_: "text".to_string(),
32            path: None,
33            content: Some(text.clone()),
34        });
35    }
36
37    for path in &args.directory {
38        sources.push(Source {
39            type_: "directory".to_string(),
40            path: Some(path.clone()),
41            content: None,
42        });
43    }
44
45    if let Some(ref stdin_type) = args.stdin {
46        let mut content = String::new();
47        std::io::stdin().read_to_string(&mut content)?;
48        sources.push(Source {
49            type_: stdin_type.clone(),
50            path: None,
51            content: Some(content),
52        });
53    }
54
55    let priority = match &args.priority {
56        Some(value) => match parse_priority_value(value) {
57            Ok(priority) => Some(priority),
58            Err(err) => {
59                eprintln!("Error: {}", err);
60                return Ok(1);
61            }
62        },
63        None => None,
64    };
65
66    let has_source = !sources.is_empty();
67    let has_description = args.description.is_some();
68    let has_title = args.title.is_some();
69
70    if !has_source && !has_description && !has_title {
71        eprintln!("Error: At least one of --description, --title, or a source is required");
72        eprintln!("Use --diff, --file, --text, --directory, --stdin, --description, or --title");
73        return Ok(1);
74    }
75
76    let metadata = match &args.metadata {
77        Some(json_str) => match serde_json::from_str(json_str) {
78            Ok(v) => v,
79            Err(e) => {
80                eprintln!("Error: Invalid JSON for metadata: {}", e);
81                return Ok(1);
82            }
83        },
84        None => serde_json::Value::Object(serde_json::Map::new()),
85    };
86
87    let blocked_by: Vec<String> = match &args.blocked_by {
88        Some(ids) => ids
89            .split(',')
90            .map(|s| s.trim().to_string())
91            .filter(|s| !s.is_empty())
92            .collect(),
93        None => Vec::new(),
94    };
95
96    let item = queue.push_with_description(
97        sources,
98        args.title.clone(),
99        args.description.clone(),
100        priority,
101        metadata,
102        blocked_by,
103    )?;
104
105    if args.json {
106        let json = serde_json::to_string_pretty(&item.to_json_value())?;
107        println!("{}", json);
108    } else {
109        println!("{}", item.id);
110        eprintln!(
111            "Added item {} with {} source(s)",
112            item.id,
113            item.sources.len()
114        );
115    }
116
117    Ok(0)
118}