mirror of
https://github.com/scottlamb/moonfire-nvr.git
synced 2025-01-26 22:23:16 -05:00
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:
parent
23c1b9404b
commit
e21f795e93
139
server/Cargo.lock
generated
139
server/Cargo.lock
generated
@ -47,15 +47,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.68"
|
||||
@ -153,6 +144,27 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bpaf"
|
||||
version = "0.7.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "863c0b21775e45ebf9bbb3a6d0cd9b3421c88a036e825359e3d4015561f3e23c"
|
||||
dependencies = [
|
||||
"bpaf_derive",
|
||||
"owo-colors",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bpaf_derive"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ed63113bfb3a9bb25dd131acdf0044c7404000ea57dd9eec1f221e3547b4748"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.1.0"
|
||||
@ -220,20 +232,6 @@ dependencies = [
|
||||
"inout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags",
|
||||
"term_size",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
version = "0.11.1"
|
||||
@ -756,15 +754,6 @@ dependencies = [
|
||||
"hashbrown 0.13.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
@ -974,6 +963,12 @@ version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11b0d96e660696543b251e58030cf9787df56da39dab19ad60eae7353040917e"
|
||||
|
||||
[[package]]
|
||||
name = "is_ci"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
@ -1163,9 +1158,9 @@ version = "0.7.5"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"blake3",
|
||||
"bpaf",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"clap",
|
||||
"cursive",
|
||||
"failure",
|
||||
"fnv",
|
||||
@ -1196,7 +1191,6 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smallvec",
|
||||
"structopt",
|
||||
"sync_wrapper",
|
||||
"tempfile",
|
||||
"time 0.1.45",
|
||||
@ -1412,6 +1406,15 @@ dependencies = [
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "owo-colors"
|
||||
version = "3.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
|
||||
dependencies = [
|
||||
"supports-color",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.4.2"
|
||||
@ -1488,30 +1491,6 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.49"
|
||||
@ -1967,36 +1946,22 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "structopt"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"lazy_static",
|
||||
"structopt-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "structopt-derive"
|
||||
version = "0.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||
|
||||
[[package]]
|
||||
name = "supports-color"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ba6faf2ca7ee42fdd458f4347ae0a9bd6bcc445ad7cb57ad82b383f18870d6f"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"is_ci",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.107"
|
||||
@ -2059,16 +2024,6 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
dependencies = [
|
||||
"term_size",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.38"
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "moonfire-nvr"
|
||||
version = "0.7.5"
|
||||
authors = ["Scott Lamb <slamb@slamb.org>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
resolver = "2"
|
||||
license-file = "../LICENSE.txt"
|
||||
rust-version = "1.64"
|
||||
@ -24,9 +24,9 @@ members = ["base", "db"]
|
||||
base = { package = "moonfire-base", path = "base" }
|
||||
base64 = "0.13.0"
|
||||
blake3 = "1.0.0"
|
||||
bpaf = { version = "0.7.8", features = ["autocomplete", "bright-color", "derive"] }
|
||||
bytes = "1"
|
||||
byteorder = "1.0"
|
||||
clap = { version = "2.33.3", default-features = false, features = ["color", "wrap_help"] }
|
||||
cursive = "0.20.0"
|
||||
db = { package = "moonfire-db", path = "db" }
|
||||
failure = "0.1.1"
|
||||
@ -53,7 +53,6 @@ rusqlite = "0.28.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
smallvec = { version = "1.7", features = ["union"] }
|
||||
structopt = { version = "0.3.13", default-features = false }
|
||||
sync_wrapper = "0.1.0"
|
||||
time = "0.1"
|
||||
tokio = { version = "1.24", features = ["macros", "rt-multi-thread", "signal", "sync", "time"] }
|
||||
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -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()],
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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>,
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -4,10 +4,10 @@
|
||||
|
||||
#![cfg_attr(all(feature = "nightly", test), feature(test))]
|
||||
|
||||
use bpaf::Bpaf;
|
||||
use log::{debug, error};
|
||||
use std::fmt::Write;
|
||||
use std::str::FromStr;
|
||||
use structopt::StructOpt;
|
||||
|
||||
mod body;
|
||||
mod cmds;
|
||||
@ -19,43 +19,50 @@ mod stream;
|
||||
mod streamer;
|
||||
mod web;
|
||||
|
||||
#[derive(StructOpt)]
|
||||
#[structopt(
|
||||
name = "moonfire-nvr",
|
||||
about = "security camera network video recorder",
|
||||
global_settings(&[clap::AppSettings::ColoredHelp])
|
||||
)]
|
||||
/// Moonfire NVR: security camera network video recorder.
|
||||
#[derive(Bpaf, Debug)]
|
||||
#[bpaf(options, version)]
|
||||
enum Args {
|
||||
/// Checks database integrity (like fsck).
|
||||
Check(cmds::check::Args),
|
||||
#[bpaf(command)]
|
||||
Check(#[bpaf(external(cmds::check::args))] cmds::check::Args),
|
||||
|
||||
/// Interactively edits configuration.
|
||||
Config(cmds::config::Args),
|
||||
#[bpaf(command)]
|
||||
Config(#[bpaf(external(cmds::config::args))] cmds::config::Args),
|
||||
|
||||
/// Initializes a database.
|
||||
Init(cmds::init::Args),
|
||||
#[bpaf(command)]
|
||||
Init(#[bpaf(external(cmds::init::args))] cmds::init::Args),
|
||||
|
||||
/// Logs in a user, returning the session cookie.
|
||||
///
|
||||
///
|
||||
/// This is a privileged command that directly accesses the database. It doesn't check the
|
||||
/// user's password and even can be used to create sessions with permissions the user doesn't
|
||||
/// have.
|
||||
Login(cmds::login::Args),
|
||||
#[bpaf(command)]
|
||||
Login(#[bpaf(external(cmds::login::args))] cmds::login::Args),
|
||||
|
||||
/// Runs the server, saving recordings and allowing web access.
|
||||
Run(cmds::run::Args),
|
||||
#[bpaf(command)]
|
||||
Run(#[bpaf(external(cmds::run::args))] cmds::run::Args),
|
||||
|
||||
/// Runs a SQLite3 shell on Moonfire NVR's index database.
|
||||
///
|
||||
///
|
||||
/// Note this locks the database to prevent simultaneous access with a running server. The
|
||||
/// server maintains cached state which could be invalidated otherwise.
|
||||
Sql(cmds::sql::Args),
|
||||
#[bpaf(command)]
|
||||
Sql(#[bpaf(external(cmds::sql::args))] cmds::sql::Args),
|
||||
|
||||
/// Translates between integer and human-readable timestamps.
|
||||
Ts(cmds::ts::Args),
|
||||
#[bpaf(command)]
|
||||
Ts(#[bpaf(external(cmds::ts::args))] cmds::ts::Args),
|
||||
|
||||
/// Upgrades to the latest database schema.
|
||||
Upgrade(cmds::upgrade::Args),
|
||||
#[bpaf(command)]
|
||||
Upgrade(#[bpaf(external(cmds::upgrade::args))] cmds::upgrade::Args),
|
||||
}
|
||||
|
||||
impl Args {
|
||||
@ -73,6 +80,11 @@ impl Args {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the default database dir, for use in argument parsing with `bpaf(fallback_with(...))`.
|
||||
fn default_db_dir() -> Result<std::path::PathBuf, std::convert::Infallible> {
|
||||
Ok("/var/lib/moonfire-nvr/db".into())
|
||||
}
|
||||
|
||||
/// Custom panic hook that logs instead of directly writing to stderr.
|
||||
///
|
||||
/// This means it includes a timestamp and is more recognizable as a serious
|
||||
@ -107,12 +119,11 @@ fn main() {
|
||||
if let Err(e) = nix::time::clock_gettime(nix::time::ClockId::CLOCK_MONOTONIC) {
|
||||
eprintln!(
|
||||
"clock_gettime failed: {e}\n\n\
|
||||
This indicates a broken environment. See the troubleshooting guide."
|
||||
This indicates a broken environment. See the troubleshooting guide."
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
let args = Args::from_args();
|
||||
let mut h = mylog::Builder::new()
|
||||
.set_format(
|
||||
::std::env::var("MOONFIRE_FORMAT")
|
||||
@ -130,6 +141,22 @@ fn main() {
|
||||
.build();
|
||||
h.clone().install().unwrap();
|
||||
|
||||
// TODO: remove this when bpaf adds more direct support for defaulting to `--help`.
|
||||
// See discussion: <https://github.com/pacak/bpaf/discussions/165>.
|
||||
if std::env::args_os().len() < 2 {
|
||||
match args().run_inner(bpaf::Args::from(&["--help"])) {
|
||||
Ok(a) => panic!("bpaf --help should not return Ok: {a:#?}"),
|
||||
Err(bpaf::ParseFailure::Stdout(msg)) => {
|
||||
print!("{msg}");
|
||||
std::process::exit(0);
|
||||
}
|
||||
Err(bpaf::ParseFailure::Stderr(msg)) => {
|
||||
eprint!("{msg}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let use_panic_hook = ::std::env::var("MOONFIRE_PANIC_HOOK")
|
||||
.map(|s| s != "false" && s != "0")
|
||||
.unwrap_or(true);
|
||||
@ -137,6 +164,9 @@ fn main() {
|
||||
std::panic::set_hook(Box::new(&panic_hook));
|
||||
}
|
||||
|
||||
let args = args().run();
|
||||
log::trace!("Parsed command-line arguments: {args:#?}");
|
||||
|
||||
let r = {
|
||||
let _a = h.async_scope();
|
||||
args.run()
|
||||
@ -152,3 +182,11 @@ fn main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn bpaf_invariants() {
|
||||
super::args().check_invariants(false);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user