1use crate::shared::parse_package;
2use bpaf::*;
3use std::{path::PathBuf, process::Command};
4
5#[derive(Debug, Clone, Bpaf)]
6#[bpaf(command, generate(parse_add))]
7pub struct Add {
9 #[bpaf(external)]
10 pub package: Option<&'static str>,
11
12 pub dev: bool,
14
15 pub build: bool,
17
18 #[bpaf(external)]
19 pub source: Source,
20}
21
22#[derive(Debug, Clone, Bpaf)]
23pub enum Source {
24 Git {
25 #[bpaf(long, argument("URI"))]
26 git: String,
28
29 #[bpaf(long, argument("BRANCH"))]
30 branch: Option<String>,
32
33 #[bpaf(long, argument("TAG"))]
34 tag: Option<String>,
36
37 #[bpaf(long, argument("TAG"))]
38 rev: Option<String>,
40 },
41 Local {
42 path: PathBuf,
44 },
45 Crates {
46 registry: Option<String>,
48
49 #[bpaf(positional("DEP"), complete(complete_available_package))]
51 name: String,
52 },
53}
54
55fn complete_available_package(name: &String) -> Vec<(String, Option<String>)> {
59 if name.is_empty() {
60 return vec![("<PACKAGE>".to_string(), None)];
61 }
62 let mut cmd = Command::new("cargo");
63 cmd.args(["search", "--limit", "100"]); cmd.arg(name);
65 let output = cmd.output().expect("Couldn't run cargo search?");
66
67 if output.status.code() == Some(0) {
68 String::from_utf8_lossy(&output.stdout)
69 .lines()
70 .filter_map(|line| {
71 if !line.starts_with(name) {
72 return None;
73 }
74 let (full_package, descr) = line.split_once(" # ")?;
75 let package = full_package.split(' ').next()?;
76 Some((package.to_owned(), Some(descr.to_owned())))
77 })
78 .collect::<Vec<_>>()
79 } else {
80 panic!("{:?}", output);
81 }
82}
83
84impl Add {
85 pub fn pass_to_cmd(&self, cmd: &mut Command) {
86 cmd.arg("add");
87 pass_arg!(cmd, self.package, "--package");
88 pass_flag!(cmd, self.dev, "--dev");
89 pass_flag!(cmd, self.build, "--build");
90 match &self.source {
91 Source::Git {
92 git,
93 branch,
94 tag,
95 rev,
96 } => {
97 pass_req_arg!(cmd, git, "--git");
98 pass_arg!(cmd, branch, "--branch");
99 pass_arg!(cmd, tag, "--tag");
100 pass_arg!(cmd, rev, "--rev");
101 }
102 Source::Local { path } => {
103 pass_req_arg!(cmd, path, "--path");
104 }
105 Source::Crates { registry, name } => {
106 cmd.arg(name);
107 pass_arg!(cmd, registry, "--registry");
108 }
109 }
110 }
111}
112
113fn package() -> impl Parser<Option<&'static str>> {
114 parse_package("Package to add a new dependency to")
115}