Skip to content

Commit 3c86427

Browse files
committed
Define a cataloger and a fetcher.
1 parent f84de35 commit 3c86427

File tree

4 files changed

+207
-23
lines changed

4 files changed

+207
-23
lines changed

Cargo.lock

Lines changed: 18 additions & 21 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ publish = true
1919
[features]
2020
default = ["all", "cli", "std"]
2121
all = ["pretty", "tracing"]
22-
cli = ["asimov-module/cli", "std", "dep:clap", "dep:clientele"]
23-
std = ["asimov-module/std", "clap?/std", "clientele?/std"]
22+
cli = ["asimov-module/cli", "dogma/clap", "std", "dep:clap", "dep:clientele"]
23+
std = ["asimov-module/std", "clap?/std", "clientele?/std", "dogma/std"]
2424
unstable = []
2525

2626
# Optional features:
@@ -29,6 +29,7 @@ tracing = ["asimov-module/tracing", "clientele?/tracing"]
2929

3030
[dependencies]
3131
asimov-module = { version = "25.0.0-dev.21", default-features = true }
32+
dogma = { version = "0.1.11", default-features = false, features = ["all"] }
3233
hex = "0.4"
3334
libaes = "0.7"
3435
pbkdf2 = "0.12"
@@ -58,6 +59,16 @@ opt-level = "z"
5859
strip = true
5960
lto = "thin"
6061

62+
[[bin]]
63+
name = "asimov-signal-cataloger"
64+
path = "src/cataloger/main.rs"
65+
required-features = ["cli"]
66+
67+
[[bin]]
68+
name = "asimov-signal-fetcher"
69+
path = "src/fetcher/main.rs"
70+
required-features = ["cli"]
71+
6172
[[bin]]
6273
name = "asimov-signal-reader"
6374
path = "src/reader/main.rs"

src/cataloger/main.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// This is free and unencumbered software released into the public domain.
2+
3+
#[cfg(not(feature = "std"))]
4+
compile_error!("asimov-signal-cataloger requires the 'std' feature");
5+
6+
use asimov_module::SysexitsError::{self, *};
7+
use clap::Parser;
8+
use clientele::StandardOptions;
9+
use dogma::{Uri, UriScheme, UriValueParser};
10+
use std::error::Error;
11+
12+
/// asimov-signal-cataloger
13+
#[derive(Debug, Parser)]
14+
#[command(arg_required_else_help = false)]
15+
struct Options {
16+
#[clap(flatten)]
17+
flags: StandardOptions,
18+
19+
/// Limit the number of resources to catalog.
20+
#[arg(value_name = "COUNT", short = 'n', long)]
21+
limit: Option<usize>,
22+
23+
/// Set the output format [default: jsonl] [possible values: jsonl]
24+
#[arg(value_name = "FORMAT", short = 'o', long)]
25+
output: Option<String>,
26+
27+
/// The Signal URL to catalog (e.g., `sgnl://signal.me/#p/...`)
28+
#[arg(value_name = "URL", value_parser = UriValueParser::new(&[
29+
UriScheme::Other("sgnl".into()),
30+
]))]
31+
url: Uri<'static>,
32+
}
33+
34+
pub fn main() -> Result<SysexitsError, Box<dyn Error>> {
35+
// Load environment variables from `.env`:
36+
asimov_module::dotenv().ok();
37+
38+
// Expand wildcards and @argfiles:
39+
let args = asimov_module::args_os()?;
40+
41+
// Parse command-line options:
42+
let options = Options::parse_from(args);
43+
44+
// Handle the `--version` flag:
45+
if options.flags.version {
46+
println!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
47+
return Ok(EX_OK);
48+
}
49+
50+
// Handle the `--license` flag:
51+
if options.flags.license {
52+
print!("{}", include_str!("../../UNLICENSE"));
53+
return Ok(EX_OK);
54+
}
55+
56+
// Locate the program path:
57+
let mut program_path = std::env::current_exe()?;
58+
program_path.set_file_name("asimov-signal-reader");
59+
if cfg!(windows) {
60+
program_path.set_extension("exe");
61+
}
62+
63+
// Construct the program arguments:
64+
let mut program_args = vec![format!(
65+
"--output={}",
66+
options.output.as_deref().unwrap_or("jsonl")
67+
)];
68+
if options.flags.verbose > 0 {
69+
program_args.push(format!("-{}", "v".repeat(options.flags.verbose as usize)));
70+
}
71+
if options.flags.debug {
72+
program_args.push("-d".into());
73+
}
74+
75+
// On Unix-like systems, use `execvp(3)` to replace the current process:
76+
#[cfg(unix)]
77+
{
78+
use std::{os::unix::process::CommandExt, process::Command};
79+
let error = Command::new(&program_path).args(program_args).exec();
80+
return Err(format!("failed to exec: {}", error).into());
81+
}
82+
83+
// On Windows or when `execvp(3)` is not available, spawn a subprocess:
84+
#[cfg(not(unix))]
85+
{
86+
use std::process::{Command, exit};
87+
let status = Command::new(&program_path).args(program_args).status()?;
88+
exit(status.code().unwrap_or(SysexitsError::EX_UNAVAILABLE));
89+
}
90+
}

src/fetcher/main.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// This is free and unencumbered software released into the public domain.
2+
3+
#[cfg(not(feature = "std"))]
4+
compile_error!("asimov-signal-fetcher requires the 'std' feature");
5+
6+
use asimov_module::SysexitsError::{self, *};
7+
use clap::Parser;
8+
use clientele::StandardOptions;
9+
use dogma::{Uri, UriScheme, UriValueParser};
10+
use std::error::Error;
11+
12+
/// asimov-signal-fetcher
13+
#[derive(Debug, Parser)]
14+
#[command(arg_required_else_help = false)]
15+
struct Options {
16+
#[clap(flatten)]
17+
flags: StandardOptions,
18+
19+
/// Set the output format [default: jsonl] [possible values: jsonl]
20+
#[arg(value_name = "FORMAT", short = 'o', long)]
21+
output: Option<String>,
22+
23+
/// The Signal URL to fetch (e.g., `sgnl://signal.me/#p/...`)
24+
#[arg(value_name = "URL", value_parser = UriValueParser::new(&[
25+
UriScheme::Other("sgnl".into()),
26+
]))]
27+
url: Uri<'static>,
28+
}
29+
30+
pub fn main() -> Result<SysexitsError, Box<dyn Error>> {
31+
// Load environment variables from `.env`:
32+
asimov_module::dotenv().ok();
33+
34+
// Expand wildcards and @argfiles:
35+
let args = asimov_module::args_os()?;
36+
37+
// Parse command-line options:
38+
let options = Options::parse_from(args);
39+
40+
// Handle the `--version` flag:
41+
if options.flags.version {
42+
println!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
43+
return Ok(EX_OK);
44+
}
45+
46+
// Handle the `--license` flag:
47+
if options.flags.license {
48+
print!("{}", include_str!("../../UNLICENSE"));
49+
return Ok(EX_OK);
50+
}
51+
52+
// Locate the program path:
53+
let mut program_path = std::env::current_exe()?;
54+
program_path.set_file_name("asimov-signal-reader");
55+
if cfg!(windows) {
56+
program_path.set_extension("exe");
57+
}
58+
59+
// Construct the program arguments:
60+
let mut program_args = vec![format!(
61+
"--output={}",
62+
options.output.as_deref().unwrap_or("jsonl")
63+
)];
64+
if options.flags.verbose > 0 {
65+
program_args.push(format!("-{}", "v".repeat(options.flags.verbose as usize)));
66+
}
67+
if options.flags.debug {
68+
program_args.push("-d".into());
69+
}
70+
71+
// On Unix-like systems, use `execvp(3)` to replace the current process:
72+
#[cfg(unix)]
73+
{
74+
use std::{os::unix::process::CommandExt, process::Command};
75+
let error = Command::new(&program_path).args(program_args).exec();
76+
return Err(format!("failed to exec: {}", error).into());
77+
}
78+
79+
// On Windows or when `execvp(3)` is not available, spawn a subprocess:
80+
#[cfg(not(unix))]
81+
{
82+
use std::process::{Command, exit};
83+
let status = Command::new(&program_path).args(program_args).status()?;
84+
exit(status.code().unwrap_or(SysexitsError::EX_UNAVAILABLE));
85+
}
86+
}

0 commit comments

Comments
 (0)