unofficial mirror of meta@public-inbox.org
 help / color / mirror / Atom feed
* [PATCH 0/3] lei rm-watch, some error handling stuff
@ 2021-07-23 10:56 Eric Wong
  2021-07-23 10:56 ` [PATCH 1/3] t/lei*: check error messages on failures Eric Wong
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Eric Wong @ 2021-07-23 10:56 UTC (permalink / raw)
  To: meta

I'm considering making "tag-ro" the default watch state
and introducing the idea of implicit watches for all
"lei q" output directories.

Eric Wong (3):
  t/lei*: check error messages on failures
  lei: avoid SQLite COUNT() for dedupe
  lei rm-watch: new command to support removing watches

 MANIFEST                          |  1 +
 lib/PublicInbox/LEI.pm            |  4 +++-
 lib/PublicInbox/LeiDedupe.pm      |  5 ++---
 lib/PublicInbox/LeiInput.pm       |  8 ++++++++
 lib/PublicInbox/LeiRmWatch.pm     | 31 +++++++++++++++++++++++++++++++
 lib/PublicInbox/LeiSavedSearch.pm |  8 ++++----
 lib/PublicInbox/LeiToMail.pm      |  4 ++--
 lib/PublicInbox/LeiXSearch.pm     |  2 +-
 lib/PublicInbox/SharedKV.pm       |  7 +++++++
 t/lei-externals.t                 |  4 ++++
 t/lei-import-http.t               |  2 ++
 t/lei-import-imap.t               |  1 +
 t/lei-mirror.t                    |  7 ++++++-
 t/lei-p2q.t                       |  1 +
 t/lei-q-kw.t                      |  1 +
 t/lei-q-remote-import.t           |  1 +
 t/lei-q-save.t                    |  4 ++++
 t/lei-tag.t                       |  8 +++++++-
 t/lei-watch.t                     | 18 ++++++++++++++----
 t/solver_git.t                    |  7 +++++++
 20 files changed, 107 insertions(+), 17 deletions(-)
 create mode 100644 lib/PublicInbox/LeiRmWatch.pm

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 1/3] t/lei*: check error messages on failures
  2021-07-23 10:56 [PATCH 0/3] lei rm-watch, some error handling stuff Eric Wong
@ 2021-07-23 10:56 ` Eric Wong
  2021-07-23 10:56 ` [PATCH 2/3] lei: avoid SQLite COUNT() for dedupe Eric Wong
  2021-07-23 10:56 ` [PATCH 3/3] lei rm-watch: new command to support removing watches Eric Wong
  2 siblings, 0 replies; 4+ messages in thread
From: Eric Wong @ 2021-07-23 10:56 UTC (permalink / raw)
  To: meta

I just hit an unreproducible failure in t/lei-p2q.t and
lacked $lei_err information to diagnose it.  Hopefully
this helps track down odd failures in the future.
---
 t/lei-externals.t       | 4 ++++
 t/lei-import-http.t     | 2 ++
 t/lei-import-imap.t     | 1 +
 t/lei-mirror.t          | 7 ++++++-
 t/lei-p2q.t             | 1 +
 t/lei-q-kw.t            | 1 +
 t/lei-q-remote-import.t | 1 +
 t/lei-q-save.t          | 4 ++++
 t/lei-tag.t             | 8 +++++++-
 t/solver_git.t          | 7 +++++++
 10 files changed, 34 insertions(+), 2 deletions(-)

diff --git a/t/lei-externals.t b/t/lei-externals.t
index f148fa3c..5e3c67bc 100644
--- a/t/lei-externals.t
+++ b/t/lei-externals.t
@@ -211,6 +211,8 @@ test_lei(sub {
 		like($lei_out, qr/use boolean prefix/, '--stdin on pipe');
 	}
 	ok(!lei(qw(q -q --stdin s:use)), "--stdin and argv don't mix");
+	like($lei_err, qr/no query allowed.*--stdin/,
+		'--stdin conflict error message');
 
 	for my $fmt (qw(ldjson ndjson jsonl)) {
 		lei_ok('q', '-f', $fmt, 's:use boolean prefix');
@@ -250,6 +252,8 @@ test_lei(sub {
 	}
 	ok(!lei('q', '-o', "$home/mbox", 's:nope'),
 			'fails if mbox format unspecified');
+	like($lei_err, qr/unable to determine mbox/, 'mbox-related message');
+
 	ok(!lei(qw(q --no-local s:see)), '--no-local');
 	is($? >> 8, 1, 'proper exit code');
 	like($lei_err, qr/no local or remote.+? to search/, 'no inbox');
diff --git a/t/lei-import-http.t b/t/lei-import-http.t
index 2104c778..e9eec1f7 100644
--- a/t/lei-import-http.t
+++ b/t/lei-import-http.t
@@ -17,6 +17,7 @@ test_lei({ tmpdir => $tmpdir }, sub {
 	my $url = "http://$host_port/t2";
 	for my $p (qw(bogus@x/t.mbox.gz bogus@x/raw ?q=noresultever)) {
 		ok(!lei('import', "$url/$p"), "/$p fails properly");
+		like($lei_err, qr/curl.*404/, 'got curl 404');
 	}
 	for my $p (qw(/ /T/ /t/ /t.atom)) {
 		ok(!lei('import', "$url/m\@example$p"), "/$p fails");
@@ -42,5 +43,6 @@ test_lei({ tmpdir => $tmpdir }, sub {
 
 	ok(!lei(qw(import --mail-sync), "$url/x\@example.com/raw"),
 		'--mail-sync fails on HTTP');
+	like($lei_err, qr/--mail-sync/, 'error message notes --mail-sync');
 });
 done_testing;
diff --git a/t/lei-import-imap.t b/t/lei-import-imap.t
index 12f6fad0..315567b3 100644
--- a/t/lei-import-imap.t
+++ b/t/lei-import-imap.t
@@ -43,6 +43,7 @@ test_lei({ tmpdir => $tmpdir }, sub {
 	$url = $u;
 	$u =~ s/;UIDVALIDITY=(\d+)\s*/;UIDVALIDITY=9$1/s;
 	ok(!lei('import', $u), 'UIDVALIDITY mismatch in URL rejected');
+	like($lei_err, qr/UIDVALIDITY mismatch/, 'mismatch noted');
 
 	lei_ok('inspect', $url);
 	my $inspect = json_utf8->decode($lei_out);
diff --git a/t/lei-mirror.t b/t/lei-mirror.t
index dfd35e1a..80bc6ed5 100644
--- a/t/lei-mirror.t
+++ b/t/lei-mirror.t
@@ -27,6 +27,7 @@ test_lei({ tmpdir => $tmpdir }, sub {
 
 	ok(!lei('add-external', $t2, '--mirror', "$http/t2/"),
 		'--mirror fails if reused') or diag "$lei_err.$lei_out = $?";
+	like($lei_err, qr/\Q$t2\E' already exists/, 'destination in error');
 
 	ok(!lei('add-external', "$home/t2\nnewline", '--mirror', "$http/t2/"),
 		'--mirror fails on newline');
@@ -37,13 +38,16 @@ test_lei({ tmpdir => $tmpdir }, sub {
 	unlike($lei_out, qr!\Qnewline\E!, 'newline entry not added');
 
 	ok(!lei('add-external', "$t2-fail", '-Lmedium'), '--mirror v2');
+	like($lei_err, qr/not a directory/, 'non-directory noted');
 	ok(!-d "$t2-fail", 'destination not created on failure');
 	lei_ok('ls-external');
 	unlike($lei_out, qr!\Q$t2-fail\E!, 'not added to ls-external');
 
 	my %phail = (
 		HTTPS => 'https://public-inbox.org/' . 'phail',
-		ONION => 'http://7fh6tueqddpjyxjmgtdiueylzoqt6pt7hec3pukyptlmohoowvhde4yd.onion/' . 'phail,'
+		ONION =>
+'http://7fh6tueqddpjyxjmgtdiueylzoqt6pt7hec3pukyptlmohoowvhde4yd.onion/' .
+'phail,'
 	);
 	for my $t (qw(HTTPS ONION)) {
 	SKIP: {
@@ -56,6 +60,7 @@ test_lei({ tmpdir => $tmpdir }, sub {
 		is($? >> 8, 22, 'curl 404');
 		ok(!-d $dir, 'directory not created');
 		unlike($lei_err, qr/# mirrored/, 'no success message');
+		like($lei_err, qr/curl.*404/, "curl 404 shown for $k");
 	} # SKIP
 	} # for
 });
diff --git a/t/lei-p2q.t b/t/lei-p2q.t
index 58506f94..495d81de 100644
--- a/t/lei-p2q.t
+++ b/t/lei-p2q.t
@@ -8,6 +8,7 @@ require_mods(qw(json DBD::SQLite Search::Xapian));
 test_lei(sub {
 	ok(!lei(qw(p2q this-better-cause-format-patch-to-fail)),
 		'p2q fails on bogus arg');
+	like($lei_err, qr/format-patch.*failed/, 'notes format-patch failure');
 	lei_ok(qw(p2q -w dfpost t/data/0001.patch));
 	is($lei_out, "dfpost:6e006fd73b1d\n", 'pathname') or diag $lei_err;
 	open my $fh, '+<', 't/data/0001.patch' or xbail "open: $!";
diff --git a/t/lei-q-kw.t b/t/lei-q-kw.t
index 528751b4..2e6be1f0 100644
--- a/t/lei-q-kw.t
+++ b/t/lei-q-kw.t
@@ -48,6 +48,7 @@ SKIP: {
 	my $cat = popen_rd(['cat', $o]);
 	ok(!lei(qw(q --import-before bogus -o), "mboxrd:$o"),
 		'--import-before fails on non-seekable output');
+	like($lei_err, qr/not seekable/, 'unseekable noted in error');
 	is(do { local $/; <$cat> }, '', 'no output on FIFO');
 	close $cat;
 	$cat = popen_rd(['cat', $o]);
diff --git a/t/lei-q-remote-import.t b/t/lei-q-remote-import.t
index 7db684d9..aaf56e27 100644
--- a/t/lei-q-remote-import.t
+++ b/t/lei-q-remote-import.t
@@ -54,6 +54,7 @@ test_lei({ tmpdir => $tmpdir }, sub {
 	ok(-f $o && -s _, '--lock=none respected') or diag $lei_err;
 	unlink $o or xbail("unlink $o $! cwd=".Cwd::getcwd());
 	ok(!lei(@cmd, '--lock=dotlock,timeout=0.000001'), 'dotlock fails');
+	like($lei_err, qr/dotlock timeout/, 'timeout noted');
 	ok(-f $o && !-s _, 'nothing output on lock failure');
 	unlink "$o.lock" or BAIL_OUT $!;
 	lei_ok(@cmd, '--lock=dotlock,timeout=0.000001',
diff --git a/t/lei-q-save.t b/t/lei-q-save.t
index 6c592088..b1ca4e92 100644
--- a/t/lei-q-save.t
+++ b/t/lei-q-save.t
@@ -69,6 +69,7 @@ test_lei(sub {
 	ok(-s "$home/mbcl2" > $size, 'size increased after up');
 
 	ok(!lei(qw(up -q), $home), 'up fails w/o --save');
+	like($lei_err, qr/--save was not used/, 'error noted --save');
 
 	lei_ok qw(ls-search); my @d = split(/\n/, $lei_out);
 	lei_ok qw(ls-search -z); my @z = split(/\0/, $lei_out);
@@ -115,6 +116,8 @@ test_lei(sub {
 	lei_ok(qw(up --all=local));
 
 	ok(!lei(qw(forget-search), "$home/bogus"), 'bogus forget');
+	like($lei_err, qr/--save was not used/, 'error noted --save');
+
 	lei_ok qw(_complete lei forget-search);
 	like($lei_out, qr/mbrd-aug/, 'forget-search completion');
 	lei_ok(qw(forget-search -v), "$home/mbrd-aug");
@@ -124,6 +127,7 @@ test_lei(sub {
 	unlike($lei_out, qr/mbrd-aug/,
 		'forget-search completion cleared after forget');
 	ok(!lei('up', "$home/mbrd-aug"), 'lei up fails after forget');
+	like($lei_err, qr/--save was not used/, 'error noted --save');
 
 	# dedupe=mid
 	my $o = "$home/dd-mid";
diff --git a/t/lei-tag.t b/t/lei-tag.t
index 5cb6d9ce..44e4659f 100644
--- a/t/lei-tag.t
+++ b/t/lei-tag.t
@@ -32,8 +32,14 @@ test_lei(sub {
 	lei_ok(qw(ls-label)); is($lei_out, "urgent\n", 'label found');
 	ok(!lei(qw(tag -F eml t/utf8.eml +kw:seeen)), 'bad kw rejected');
 	like($lei_err, qr/`seeen' is not one of/, 'got helpful error');
+
 	ok(!lei(qw(tag -F eml t/utf8.eml +k:seen)), 'bad prefix rejected');
+	like($lei_err, qr/Unable to handle.*\Q+k:seen\E/, 'bad prefix noted');
+
 	ok(!lei(qw(tag -F eml t/utf8.eml)), 'no keywords');
+	like($lei_err, qr/no keywords or labels specified/,
+		'lack of kw/L noted');
+
 	my $mb = "$ENV{HOME}/mb";
 	my $md = "$ENV{HOME}/md";
 	lei_ok(qw(q m:testmessage@example.com -o), "mboxrd:$mb");
@@ -78,7 +84,7 @@ test_lei(sub {
 	lei_ok(qw(ls-label));
 	is($lei_out, "nope\nqp\nurgent\n", 'ls-label shows qp');
 
-	lei_ok qw(tag -F eml t/utf8.eml +L:INBOX +L:x); diag $lei_err;
+	lei_ok qw(tag -F eml t/utf8.eml +L:INBOX +L:x);
 	lei_ok qw(q m:testmessage@example.com);
 	$check_kw->([qw(answered seen)], L => [qw(INBOX nope urgent x)]);
 	lei_ok(qw(ls-label));
diff --git a/t/solver_git.t b/t/solver_git.t
index fe1aff0b..f5cc592c 100644
--- a/t/solver_git.t
+++ b/t/solver_git.t
@@ -43,6 +43,7 @@ test_lei({tmpdir => "$tmpdir/blob"}, sub {
 	is($lei_out, $patch2->as_string, 'blob matches');
 	ok(!lei('blob', '--mail', '69df7d5', '-I', $ibx->{inboxdir}),
 		"--mail won't run solver");
+	like($lei_err, qr/\b69df7d5\b/, 'OID in error by git(1)');
 
 	lei_ok('blob', '69df7d5', '-I', $ibx->{inboxdir});
 	is(git_sha(1, \$lei_out)->hexdigest, $expect, 'blob contents output');
@@ -51,6 +52,8 @@ test_lei({tmpdir => "$tmpdir/blob"}, sub {
 	is($lei_out, $prev, '--no-mail works');
 	ok(!lei(qw(blob -I), $ibx->{inboxdir}, $non_existent),
 			'non-existent blob fails');
+	my $abbrev = substr($non_existent, 0, 7);
+	like($lei_err, qr/could not find $abbrev/, 'failed abbreviation noted');
 	SKIP: {
 		skip '/.git exists', 1 if -e '/.git';
 		lei_ok(qw(-C / blob 69df7d5 -I), $ibx->{inboxdir},
@@ -59,9 +62,13 @@ test_lei({tmpdir => "$tmpdir/blob"}, sub {
 
 		ok(!lei(qw(-C / blob --no-cwd 69df7d5 -I), $ibx->{inboxdir}),
 			'--no-cwd works');
+		like($lei_err, qr/no --git-dir to try/,
+			'lack of --git-dir noted');
 
 		ok(!lei(qw(-C / blob -I), $ibx->{inboxdir}, $non_existent),
 			'non-existent blob fails');
+		like($lei_err, qr/no --git-dir to try/,
+			'lack of --git-dir noted');
 	}
 
 	# fallbacks

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 2/3] lei: avoid SQLite COUNT() for dedupe
  2021-07-23 10:56 [PATCH 0/3] lei rm-watch, some error handling stuff Eric Wong
  2021-07-23 10:56 ` [PATCH 1/3] t/lei*: check error messages on failures Eric Wong
@ 2021-07-23 10:56 ` Eric Wong
  2021-07-23 10:56 ` [PATCH 3/3] lei rm-watch: new command to support removing watches Eric Wong
  2 siblings, 0 replies; 4+ messages in thread
From: Eric Wong @ 2021-07-23 10:56 UTC (permalink / raw)
  To: meta

SQLite COUNT() is a slow operation that does a full table scan
with no conditions.  There's no need for it, since lei dedupe
only needs to know if it's empty or not to decide between
new/ and cur/ for Maildir outputs.
---
 lib/PublicInbox/LeiDedupe.pm      | 5 ++---
 lib/PublicInbox/LeiSavedSearch.pm | 8 ++++----
 lib/PublicInbox/LeiToMail.pm      | 4 ++--
 lib/PublicInbox/LeiXSearch.pm     | 2 +-
 lib/PublicInbox/SharedKV.pm       | 7 +++++++
 5 files changed, 16 insertions(+), 10 deletions(-)

diff --git a/lib/PublicInbox/LeiDedupe.pm b/lib/PublicInbox/LeiDedupe.pm
index ed52e417..32f99cd0 100644
--- a/lib/PublicInbox/LeiDedupe.pm
+++ b/lib/PublicInbox/LeiDedupe.pm
@@ -127,10 +127,9 @@ sub pause_dedupe {
 	delete($skv->{dbh}) if $skv;
 }
 
-sub dedupe_nr {
+sub has_entries {
 	my $skv = $_[0]->[0] or return undef;
-	my @n = $skv->count;
-	$n[0];
+	$skv->has_entries;
 }
 
 1;
diff --git a/lib/PublicInbox/LeiSavedSearch.pm b/lib/PublicInbox/LeiSavedSearch.pm
index 929380ed..cfbf68c3 100644
--- a/lib/PublicInbox/LeiSavedSearch.pm
+++ b/lib/PublicInbox/LeiSavedSearch.pm
@@ -315,11 +315,11 @@ E: rename($dir_old, $dir_new) error: $!
 EOM
 }
 
-# cf. LeiDedupe->dedupe_nr
-sub dedupe_nr {
+# cf. LeiDedupe->has_entries
+sub has_entries {
 	my $oidx = $_[0]->{oidx} // die 'BUG: no {oidx}';
-	my @n = $oidx->{dbh}->selectrow_array('SELECT COUNT(*) FROM over');
-	$n[0];
+	my @n = $oidx->{dbh}->selectrow_array('SELECT num FROM over LIMIT 1');
+	scalar(@n) ? 1 : undef;
 }
 
 no warnings 'once';
diff --git a/lib/PublicInbox/LeiToMail.pm b/lib/PublicInbox/LeiToMail.pm
index b9405c0c..d782d4c7 100644
--- a/lib/PublicInbox/LeiToMail.pm
+++ b/lib/PublicInbox/LeiToMail.pm
@@ -198,7 +198,7 @@ sub _mbox_write_cb ($$) {
 	my $dedupe = $lei->{dedupe};
 	$dedupe->prepare_dedupe;
 	my $lse = $lei->{lse}; # may be undef
-	my $set_recent = $dedupe->dedupe_nr;
+	my $set_recent = $dedupe->has_entries;
 	sub { # for git_to_mail
 		my ($buf, $smsg, $eml) = @_;
 		$eml //= PublicInbox::Eml->new($buf);
@@ -293,7 +293,7 @@ sub _maildir_write_cb ($$) {
 	# Favor cur/ and only write to new/ when augmenting.  This
 	# saves MUAs from having to do a mass rename when the initial
 	# search result set is huge.
-	my $dir = $dedupe && $dedupe->dedupe_nr ? 'new/' : 'cur/';
+	my $dir = $dedupe && $dedupe->has_entries ? 'new/' : 'cur/';
 	sub { # for git_to_mail
 		my ($bref, $smsg, $eml) = @_;
 		$dst // return $lei->fail; # dst may be undef-ed in last run
diff --git a/lib/PublicInbox/LeiXSearch.pm b/lib/PublicInbox/LeiXSearch.pm
index cac7fb7d..3414e87d 100644
--- a/lib/PublicInbox/LeiXSearch.pm
+++ b/lib/PublicInbox/LeiXSearch.pm
@@ -504,7 +504,7 @@ sub do_query {
 		my $F_SETPIPE_SZ = $^O eq 'linux' ? 1031 : undef;
 		if ($l2m->{-wq_nr_workers} > 1 &&
 				$l2m->{base_type} =~ /\A(?:maildir|mbox)\z/) {
-			# setup two barriers to coordinate dedupe_nr
+			# setup two barriers to coordinate ->has_entries
 			# between l2m workers
 			pipe(my ($a_r, $a_w)) or die "pipe: $!";
 			fcntl($a_r, $F_SETPIPE_SZ, 4096) if $F_SETPIPE_SZ;
diff --git a/lib/PublicInbox/SharedKV.pm b/lib/PublicInbox/SharedKV.pm
index 8347b195..3487e820 100644
--- a/lib/PublicInbox/SharedKV.pm
+++ b/lib/PublicInbox/SharedKV.pm
@@ -154,6 +154,13 @@ SELECT COUNT(k) FROM kv
 	$sth->fetchrow_array;
 }
 
+# faster than ->count due to how SQLite works
+sub has_entries {
+	my ($self) = @_;
+	my @n = $self->{dbh}->selectrow_array('SELECT k FROM kv LIMIT 1');
+	scalar(@n) ? 1 : undef;
+}
+
 sub dbh_release {
 	my ($self, $lock) = @_;
 	my $dbh = delete $self->{dbh} or return;

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 3/3] lei rm-watch: new command to support removing watches
  2021-07-23 10:56 [PATCH 0/3] lei rm-watch, some error handling stuff Eric Wong
  2021-07-23 10:56 ` [PATCH 1/3] t/lei*: check error messages on failures Eric Wong
  2021-07-23 10:56 ` [PATCH 2/3] lei: avoid SQLite COUNT() for dedupe Eric Wong
@ 2021-07-23 10:56 ` Eric Wong
  2 siblings, 0 replies; 4+ messages in thread
From: Eric Wong @ 2021-07-23 10:56 UTC (permalink / raw)
  To: meta

Pretty trivial since it just invokes "git-config".  It's mainly
intended to make shell completion easier.
---
 MANIFEST                      |  1 +
 lib/PublicInbox/LEI.pm        |  4 +++-
 lib/PublicInbox/LeiInput.pm   |  8 ++++++++
 lib/PublicInbox/LeiRmWatch.pm | 31 +++++++++++++++++++++++++++++++
 t/lei-watch.t                 | 18 ++++++++++++++----
 5 files changed, 57 insertions(+), 5 deletions(-)
 create mode 100644 lib/PublicInbox/LeiRmWatch.pm

diff --git a/MANIFEST b/MANIFEST
index 1d79b7c9..a3913501 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -233,6 +233,7 @@ lib/PublicInbox/LeiQuery.pm
 lib/PublicInbox/LeiRediff.pm
 lib/PublicInbox/LeiRemote.pm
 lib/PublicInbox/LeiRm.pm
+lib/PublicInbox/LeiRmWatch.pm
 lib/PublicInbox/LeiSavedSearch.pm
 lib/PublicInbox/LeiSearch.pm
 lib/PublicInbox/LeiSelfSocket.pm
diff --git a/lib/PublicInbox/LEI.pm b/lib/PublicInbox/LEI.pm
index 52c551cf..191a0790 100644
--- a/lib/PublicInbox/LEI.pm
+++ b/lib/PublicInbox/LEI.pm
@@ -232,8 +232,10 @@ our %CMD = ( # sorted in order of importance/use:
 	'remove imported messages from IMAP, Maildirs, and MH',
 	qw(exact! all jobs:i indexed), @c_opt ],
 
-'add-watch' => [ 'LOCATION', 'watch for new messages and flag changes',
+'add-watch' => [ 'LOCATION...', 'watch for new messages and flag changes',
 	qw(poll-interval=s state=s recursive|r), @c_opt ],
+'rm-watch' => [ 'LOCATION...', 'remove specified watch(es)',
+	qw(recursive|r), @c_opt ],
 'ls-watch' => [ '[FILTER...]', 'list active watches with numbers and status',
 		qw(l z|0), @c_opt ],
 'pause-watch' => [ '[WATCH_NUMBER_OR_FILTER]', qw(all local remote), @c_opt ],
diff --git a/lib/PublicInbox/LeiInput.pm b/lib/PublicInbox/LeiInput.pm
index fa330df5..88889f45 100644
--- a/lib/PublicInbox/LeiInput.pm
+++ b/lib/PublicInbox/LeiInput.pm
@@ -270,6 +270,10 @@ sub prepare_inputs { # returns undef on error
 				$sync and $input = 'maildir:'.
 						$lei->abs_path($input_path);
 				push @md, $input;
+			} elsif ($self->{missing_ok} && !-e _) {
+				# for "lei rm-watch" on missing Maildir
+				$sync and $input = 'maildir:'.
+						$lei->abs_path($input_path);
 			} else {
 				return $lei->fail("Unable to handle $input");
 			}
@@ -305,6 +309,10 @@ $input is `eml', not --in-format=$in_fmt
 					push @{$sync->{ok}}, $input;
 				}
 				push @md, $input;
+			} elsif ($self->{missing_ok} && !-e $input) {
+				# for lei rm-watch
+				$sync and $input = 'maildir:'.
+						$lei->abs_path($input);
 			} else {
 				return $lei->fail("Unable to handle $input")
 			}
diff --git a/lib/PublicInbox/LeiRmWatch.pm b/lib/PublicInbox/LeiRmWatch.pm
new file mode 100644
index 00000000..c0f336f0
--- /dev/null
+++ b/lib/PublicInbox/LeiRmWatch.pm
@@ -0,0 +1,31 @@
+# Copyright all contributors <meta@public-inbox.org>
+# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
+
+# "lei rm-watch" command
+package PublicInbox::LeiRmWatch;
+use strict;
+use v5.10.1;
+use parent qw(PublicInbox::LeiInput);
+
+sub lei_rm_watch {
+	my ($lei, @argv) = @_;
+	my $cfg = $lei->_lei_cfg(1);
+	$lei->{opt}->{'mail-sync'} = 1; # for prepare_inputs
+	my $self = bless { missing_ok => 1 }, __PACKAGE__;
+	$self->prepare_inputs($lei, \@argv) or return;
+	for my $w (@{$self->{inputs}}) {
+		$lei->_config('--remove-section', "watch.$w");
+	}
+	delete $lei->{cfg}; # force reload
+	$lei->refresh_watches;
+}
+
+sub _complete_rm_watch {
+	my ($lei, @argv) = @_;
+	my $cfg = $lei->_lei_cfg or return;
+	my $match_cb = $lei->complete_url_prepare(\@argv);
+	my @w = (join("\n", keys %$cfg) =~ m/^watch\.(.+?)\.state$/sgm);
+	map { $match_cb->($_) } @w;
+}
+
+1;
diff --git a/t/lei-watch.t b/t/lei-watch.t
index 492f6c1d..9a3bfd80 100644
--- a/t/lei-watch.t
+++ b/t/lei-watch.t
@@ -2,7 +2,7 @@
 # Copyright all contributors <meta@public-inbox.org>
 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
 use strict; use v5.10.1; use PublicInbox::TestCommon;
-use File::Path qw(make_path);
+use File::Path qw(make_path remove_tree);
 require_mods('lei');
 my $have_fast_inotify = eval { require Linux::Inotify2 } ||
 	eval { require IO::KQueue };
@@ -71,9 +71,19 @@ test_lei(sub {
 			'inotify has Maildir watches');
 	}
 
-	is(xsys(qw(git config -f), $cfg_f,
-			'--remove-section', "watch.maildir:$md"),
-		0, 'unset config state');
+	lei_ok 'rm-watch', $md;
+	lei_ok 'ls-watch', \'refresh watches';
+	is($lei_out, '', 'no watches left');
+
+	lei_ok 'add-watch', $md2;
+	remove_tree($md2);
+	lei_ok 'rm-watch', "maildir:".$md2, \'with maildir: prefix';
+	lei_ok 'ls-watch', \'refresh watches';
+	is($lei_out, '', 'no watches left');
+
+	lei_ok 'add-watch', $md;
+	remove_tree($md);
+	lei_ok 'rm-watch', $md, \'absolute path w/ missing dir';
 	lei_ok 'ls-watch', \'refresh watches';
 	is($lei_out, '', 'no watches left');
 

^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2021-07-23 10:56 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-23 10:56 [PATCH 0/3] lei rm-watch, some error handling stuff Eric Wong
2021-07-23 10:56 ` [PATCH 1/3] t/lei*: check error messages on failures Eric Wong
2021-07-23 10:56 ` [PATCH 2/3] lei: avoid SQLite COUNT() for dedupe Eric Wong
2021-07-23 10:56 ` [PATCH 3/3] lei rm-watch: new command to support removing watches Eric Wong

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).