1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
#![cfg_attr(not(test), warn(clippy::pedantic))]
#![allow(clippy::large_futures, clippy::module_name_repetitions)]
// better solution to dead_code error on multiple binaries than https://stackoverflow.com/a/66196291
#![allow(dead_code)]
#![cfg_attr(not(test), warn(clippy::expect_used))]
use core::str;
use std::{
collections::HashSet,
env, io,
path::{Path, PathBuf},
str::FromStr,
};
use anyhow::{bail, Context, Result};
use client::{consolidate_fetch_reports, get_repo_ref_from_cache, Connect};
use git::{nostr_url::NostrUrlDecoded, RepoActions};
use ngit::{client, git};
use nostr::nips::nip01::Coordinate;
use utils::read_line;
use crate::{client::Client, git::Repo};
mod fetch;
mod list;
mod push;
mod utils;
#[tokio::main]
async fn main() -> Result<()> {
let args = env::args();
let args = args.skip(1).take(2).collect::<Vec<_>>();
let ([_, nostr_remote_url] | [nostr_remote_url]) = args.as_slice() else {
bail!("invalid arguments - no url");
};
if env::args().nth(1).as_deref() == Some("--version") {
const VERSION: &str = env!("CARGO_PKG_VERSION");
println!("v{VERSION}");
return Ok(());
}
let git_repo = Repo::from_path(&PathBuf::from(
std::env::var("GIT_DIR").context("git should set GIT_DIR when remote helper is called")?,
))?;
let git_repo_path = git_repo.get_path()?;
let client = Client::default();
let decoded_nostr_url =
NostrUrlDecoded::from_str(nostr_remote_url).context("invalid nostr url")?;
fetching_with_report_for_helper(git_repo_path, &client, &decoded_nostr_url.coordinates).await?;
let repo_ref = get_repo_ref_from_cache(git_repo_path, &decoded_nostr_url.coordinates).await?;
let stdin = io::stdin();
let mut line = String::new();
let mut list_outputs = None;
loop {
let tokens = read_line(&stdin, &mut line)?;
match tokens.as_slice() {
["capabilities"] => {
println!("option");
println!("push");
println!("fetch");
println!();
}
["option", "verbosity"] => {
println!("ok");
}
["option", ..] => {
println!("unsupported");
}
["fetch", oid, refstr] => {
fetch::run_fetch(&git_repo, &repo_ref, &decoded_nostr_url, &stdin, oid, refstr).await?;
}
["push", refspec] => {
push::run_push(
&git_repo,
&repo_ref,
nostr_remote_url,
&stdin,
refspec,
&client,
list_outputs.clone(),
)
.await?;
}
["list"] => {
list_outputs = Some(list::run_list(&git_repo, &repo_ref, false).await?);
}
["list", "for-push"] => {
list_outputs = Some(list::run_list(&git_repo, &repo_ref, true).await?);
}
[] => {
return Ok(());
}
_ => {
bail!(format!("unknown command: {}", line.trim().to_owned()));
}
}
}
}
async fn fetching_with_report_for_helper(
git_repo_path: &Path,
client: &Client,
repo_coordinates: &HashSet<Coordinate>,
) -> Result<()> {
let term = console::Term::stderr();
term.write_line("nostr: fetching...")?;
let (relay_reports, progress_reporter) = client
.fetch_all(git_repo_path, repo_coordinates, &HashSet::new())
.await?;
if !relay_reports.iter().any(std::result::Result::is_err) {
let _ = progress_reporter.clear();
term.clear_last_lines(1)?;
}
let report = consolidate_fetch_reports(relay_reports);
if report.to_string().is_empty() {
term.write_line("nostr: no updates")?;
} else {
term.write_line(&format!("nostr updates: {report}"))?;
}
Ok(())
}
|