switch from ancient clap/structopt release to bpaf

Improves #70: this reduces binary size from 12.3 MiB to 11.9 MiB (3%) on
macOS/arm64.

The user experience is almost the same. (The help output's `Usage:`
lines lack the e.g. `moonfire-nvr run` prefix of argv[0] and subcommand,
which isn't ideal, but I guess it's pretty minor in the grand scheme of
things.)
This commit is contained in:
Scott Lamb
2023-02-11 11:38:12 -08:00
parent 23c1b9404b
commit e21f795e93
12 changed files with 229 additions and 217 deletions

View File

@@ -4,45 +4,39 @@
//! Subcommand to check the database and sample file dir for errors.
use bpaf::Bpaf;
use db::check;
use failure::Error;
use std::path::PathBuf;
use structopt::StructOpt;
#[derive(StructOpt)]
#[derive(Bpaf, Debug)]
pub struct Args {
/// Directory holding the SQLite3 index database.
#[structopt(
long,
default_value = "/var/lib/moonfire-nvr/db",
value_name = "path",
parse(from_os_str)
)]
///
/// default: `/var/lib/moonfire-nvr/db`.
#[bpaf(argument("PATH"), fallback_with(crate::default_db_dir))]
db_dir: PathBuf,
/// Compare sample file lengths on disk to the database.
#[structopt(long)]
/// Compares sample file lengths on disk to the database.
compare_lens: bool,
/// Trash sample files without matching recording rows in the database.
/// This addresses "Missing ... row" errors.
/// Trashes sample files without matching recording rows in the database.
/// This addresses `Missing ... row` errors.
///
/// The ids are added to the "garbage" table to indicate the files need to
/// The ids are added to the `garbage` table to indicate the files need to
/// be deleted. Garbage is collected on normal startup.
#[structopt(long)]
trash_orphan_sample_files: bool,
/// Delete recording rows in the database without matching sample files.
/// This addresses "Recording ... missing file" errors.
#[structopt(long)]
/// Deletes recording rows in the database without matching sample files.
///
/// This addresses `Recording ... missing file` errors.
delete_orphan_rows: bool,
/// Trash recordings when their database rows appear corrupt.
/// Trashes recordings when their database rows appear corrupt.
/// This addresses "bad video_index" errors.
///
/// The ids are added to the "garbage" table to indicate their files need to
/// The ids are added to the `garbage` table to indicate their files need to
/// be deleted. Garbage is collected on normal startup.
#[structopt(long)]
trash_corrupt_rows: bool,
}

View File

@@ -8,26 +8,23 @@
//! configuration will likely be almost entirely done through a web-based UI.
use base::clock;
use bpaf::Bpaf;
use cursive::views;
use cursive::Cursive;
use failure::Error;
use std::path::PathBuf;
use std::sync::Arc;
use structopt::StructOpt;
mod cameras;
mod dirs;
mod users;
#[derive(StructOpt)]
#[derive(Bpaf, Debug)]
pub struct Args {
/// Directory holding the SQLite3 index database.
#[structopt(
long,
default_value = "/var/lib/moonfire-nvr/db",
value_name = "path",
parse(from_os_str)
)]
///
/// default: `/var/lib/moonfire-nvr/db`.
#[bpaf(argument("PATH"), fallback_with(crate::default_db_dir))]
db_dir: PathBuf,
}

View File

@@ -2,20 +2,17 @@
// Copyright (C) 2020 The Moonfire NVR Authors; see AUTHORS and LICENSE.txt.
// SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception.
use bpaf::Bpaf;
use failure::Error;
use log::info;
use std::path::PathBuf;
use structopt::StructOpt;
#[derive(StructOpt)]
#[derive(Bpaf, Debug)]
pub struct Args {
/// Directory holding the SQLite3 index database.
#[structopt(
long,
default_value = "/var/lib/moonfire-nvr/db",
value_name = "path",
parse(from_os_str)
)]
///
/// default: `/var/lib/moonfire-nvr/db`.
#[bpaf(argument("PATH"), fallback_with(crate::default_db_dir))]
db_dir: PathBuf,
}

View File

@@ -5,51 +5,58 @@
//! Subcommand to login a user (without requiring a password).
use base::clock::{self, Clocks};
use bpaf::Bpaf;
use db::auth::SessionFlag;
use failure::{format_err, Error};
use std::io::Write as _;
use std::os::unix::fs::OpenOptionsExt as _;
use std::path::PathBuf;
use structopt::StructOpt;
use std::str::FromStr;
#[derive(Debug, Default, StructOpt)]
fn parse_perms(perms: String) -> Result<db::Permissions, protobuf::text_format::ParseError> {
protobuf::text_format::parse_from_str(&perms)
}
fn parse_flags(flags: String) -> Result<Vec<SessionFlag>, Error> {
flags.split(',').map(SessionFlag::from_str).collect()
}
#[derive(Bpaf, Debug)]
pub struct Args {
/// Directory holding the SQLite3 index database.
#[structopt(
long,
default_value = "/var/lib/moonfire-nvr/db",
value_name = "path",
parse(from_os_str)
)]
///
/// default: `/var/lib/moonfire-nvr/db`.
#[bpaf(argument("PATH"), fallback_with(crate::default_db_dir))]
db_dir: PathBuf,
/// Create a session with the given permissions.
/// Creates a session with the given permissions.
///
/// If unspecified, uses user's default permissions.
#[structopt(long, value_name="perms",
parse(try_from_str = protobuf::text_format::parse_from_str))]
#[bpaf(argument::<String>("PERMS"), parse(parse_perms), optional)]
permissions: Option<db::Permissions>,
/// Restrict this cookie to the given domain.
#[structopt(long)]
/// Restricts this cookie to the given domain.
#[bpaf(argument("DOMAIN"))]
domain: Option<String>,
/// Write the cookie to a new curl-compatible cookie-jar file.
/// Writes the cookie to a new curl-compatible cookie-jar file.
///
/// ---domain must be specified. This file can be used later with curl's --cookie flag.
#[structopt(long, requires("domain"), value_name = "path")]
/// `--domain` must be specified. This file can be used later with curl's `--cookie` flag.
#[bpaf(argument("PATH"))]
curl_cookie_jar: Option<PathBuf>,
/// Set the given db::auth::SessionFlags.
#[structopt(
long,
default_value = "http-only,secure,same-site,same-site-strict",
value_name = "flags",
use_delimiter = true
/// Sets the given db::auth::SessionFlags.
///
/// default: `http-only,secure,same-site,same-site-strict`.
#[bpaf(
argument::<String>("FLAGS"),
fallback_with(|| Ok::<_, std::convert::Infallible>("http-only,secure,same-site,same-site-strict".to_owned())),
parse(parse_flags),
)]
session_flags: Vec<SessionFlag>,
/// Create the session for this username.
#[bpaf(argument("USERNAME"))]
username: String,
}
@@ -87,7 +94,7 @@ pub fn run(args: Args) -> Result<i32, Error> {
let d = args
.domain
.as_ref()
.ok_or_else(|| format_err!("--cookiejar requires --domain"))?;
.ok_or_else(|| format_err!("--curl-cookie-jar requires --domain"))?;
let mut f = std::fs::OpenOptions::new()
.write(true)
.create_new(true)

View File

@@ -27,6 +27,9 @@ pub struct ConfigFile {
pub binds: Vec<BindConfig>,
/// Directory holding the SQLite3 index database.
///
///
/// default: `/var/lib/moonfire-nvr/db`.
#[serde(default = "default_db_dir")]
pub db_dir: PathBuf,

View File

@@ -6,6 +6,7 @@ use crate::streamer;
use crate::web;
use crate::web::accept::Listener;
use base::clock;
use bpaf::Bpaf;
use db::{dir, writer};
use failure::{bail, Error, ResultExt};
use fnv::FnvHashMap;
@@ -18,23 +19,24 @@ use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
use std::thread;
use structopt::StructOpt;
use tokio::signal::unix::{signal, SignalKind};
use self::config::ConfigFile;
mod config;
#[derive(StructOpt)]
#[derive(Bpaf, Debug)]
pub struct Args {
#[structopt(short, long, default_value = "/etc/moonfire-nvr.toml")]
/// Path to configuration file.
///
/// default: `/etc/moonfire-nvr.toml`. See `ref/config.md` for config file documentation.
#[bpaf(short, long, argument("PATH"), fallback_with(|| Ok::<_, Error>("/etc/moonfire-nvr.toml".into())))]
config: PathBuf,
/// Open the database in read-only mode and disables recording.
/// Opens the database in read-only mode and disables recording.
///
/// Note this is incompatible with session authentication; consider adding
/// a bind with `allow_unauthenticated_permissions` to your config.
#[structopt(long)]
/// a bind with `allowUnauthenticatedPermissions` to your config.
read_only: bool,
}

View File

@@ -5,35 +5,31 @@
//! Subcommand to run a SQLite shell.
use super::OpenMode;
use bpaf::Bpaf;
use failure::Error;
use std::ffi::OsString;
use std::os::unix::process::CommandExt;
use std::path::PathBuf;
use std::process::Command;
use structopt::StructOpt;
#[derive(StructOpt)]
#[derive(Bpaf, Debug, PartialEq, Eq)]
pub struct Args {
/// Directory holding the SQLite3 index database.
#[structopt(
long,
default_value = "/var/lib/moonfire-nvr/db",
value_name = "path",
parse(from_os_str)
)]
///
/// default: `/var/lib/moonfire-nvr/db`.
#[bpaf(fallback_with(crate::default_db_dir))]
db_dir: PathBuf,
/// Opens the database in read-only mode and locks it only for shared access.
///
/// This can be run simultaneously with "moonfire-nvr run --read-only".
#[structopt(long)]
/// This can be run simultaneously with `moonfire-nvr run --read-only`.
read_only: bool,
/// Arguments to pass to sqlite3.
///
/// Use the -- separator to pass sqlite3 options, as in
/// "moonfire-nvr sql -- -line 'select username from user'".
#[structopt(parse(from_os_str))]
/// Use the `--` separator to pass sqlite3 options, as in
/// `moonfire-nvr sql -- -line 'select username from user'`.
#[bpaf(positional)]
arg: Vec<OsString>,
}
@@ -58,3 +54,32 @@ pub fn run(args: Args) -> Result<i32, Error> {
.exec()
.into())
}
#[cfg(test)]
mod tests {
use super::*;
use bpaf::Parser;
#[test]
fn parse_args() {
let args = args()
.to_options()
.run_inner(bpaf::Args::from(&[
"--db-dir",
"/foo/bar",
"--",
"-line",
"select username from user",
]))
.unwrap();
assert_eq!(
args,
Args {
db_dir: "/foo/bar".into(),
read_only: false, // default
arg: vec!["-line".into(), "select username from user".into()],
}
);
}
}

View File

@@ -2,18 +2,18 @@
// Copyright (C) 2020 The Moonfire NVR Authors; see AUTHORS and LICENSE.txt.
// SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception.
use bpaf::Bpaf;
use failure::Error;
use structopt::StructOpt;
#[derive(StructOpt)]
#[derive(Bpaf, Debug)]
pub struct Args {
/// Timestamp(s) to translate.
///
/// May be either an integer or an RFC-3339-like string:
/// `YYYY-mm-dd[THH:MM[:SS[:FFFFF]]][{Z,{+,-,}HH:MM}]`.
///
/// Eg: `142913484000000`, `2020-04-26`, `2020-04-26T12:00:00:00000-07:00`.
#[structopt(required = true)]
/// E.g.: `142913484000000`, `2020-04-26`, `2020-04-26T12:00:00:00000-07:00`.
#[bpaf(positional("TS"), some("must specify at least one timestamp"))]
timestamps: Vec<String>,
}

View File

@@ -2,41 +2,36 @@
// Copyright (C) 2020 The Moonfire NVR Authors; see AUTHORS and LICENSE.txt.
// SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception.
use bpaf::Bpaf;
/// Upgrades the database schema.
///
/// See `guide/schema.md` for more information.
use failure::Error;
use structopt::StructOpt;
#[derive(StructOpt)]
#[derive(Bpaf, Debug)]
pub struct Args {
#[structopt(
long,
help = "Directory holding the SQLite3 index database.",
default_value = "/var/lib/moonfire-nvr/db",
parse(from_os_str)
)]
/// Directory holding the SQLite3 index database.
///
///
/// default: `/var/lib/moonfire-nvr/db`.
#[bpaf(argument("PATH"), fallback_with(crate::default_db_dir))]
db_dir: std::path::PathBuf,
#[structopt(
help = "When upgrading from schema version 1 to 2, the sample file directory.",
long,
parse(from_os_str)
)]
/// When upgrading from schema version 1 to 2, the sample file directory.
#[bpaf(argument("PATH"))]
sample_file_dir: Option<std::path::PathBuf>,
#[structopt(
help = "Resets the SQLite journal_mode to the specified mode prior to \
the upgrade. The default, delete, is recommended. off is very \
dangerous but may be desirable in some circumstances. See \
guide/schema.md for more information. The journal mode will be \
reset to wal after the upgrade.",
long,
default_value = "delete"
)]
/// Resets the SQLite journal_mode to the specified mode prior to
/// the upgrade.
///
///
/// default: `delete` (recommended). `off` is very dangerous but may be
/// desirable in some circumstances. See `guide/schema.md` for more
/// information. The journal mode will be reset to `wal` after the upgrade.
#[bpaf(argument("MODE"), fallback_with(|| Ok::<_, std::convert::Infallible>("delete".into())))]
preset_journal: String,
#[structopt(help = "Skips the normal post-upgrade vacuum operation.", long)]
/// Skips the normal post-upgrade vacuum operation.
no_vacuum: bool,
}