Something went wrong. Try again.
atproto git client
Something went wrong. Try again.
Rust
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196//! The three rules in `docs/module-layout.md`, checked, and the one//! constraint its `config/` section adds.//!//! Three of the four cannot be expressed as visibility, which is why this//! file exists rather than being a `pub(…)` somewhere://!//! - **`lexicon/` imports nothing from `clients/`** — `clients` has to be//! `pub(crate)` for `cmd/` to call it, and `pub(crate)` is visible to//! `lexicon/` too. There is no modifier for "visible to `cmd` but not//! `lexicon`" inside one crate; only a workspace split would do it.//! - **`clients/git/` owns every interaction with git** — a `Command` is//! `std`'s, and no modifier on anything in this crate can stop another//! module building one of its own.//! - **No client asks `config/` who we are** — same shape, same reason.//!//! The remaining rule — *nothing outside `cmd/` imports `cmd/`* — **is**//! enforced by the compiler now: everything in `cmd/` is `pub(super)`, `pub(in//! crate::cmd)` or, for the entry points `main.rs` dispatches through,//! `pub(crate)`. It is checked here anyway, because the compiler only stops//! the ones that are not `pub(crate)`, and the entry points are.//!//! # Why a test and not a review habit//!//! Every one of these was violated in the tree at some point while nobody was//! looking, and each was found by grep rather than by anything failing. A rule//! that only a careful reader enforces is a rule that holds until the day it//! matters.
use std::path::{Path, PathBuf};
/// `src/`, from wherever cargo runs this.fn src() -> PathBuf { Path::new(env!("CARGO_MANIFEST_DIR")).join("src")}
fn rust_files(dir: &Path, out: &mut Vec<PathBuf>) { let entries = std::fs::read_dir(dir).unwrap_or_else(|e| panic!("read {}: {e}", dir.display())); for entry in entries.filter_map(Result::ok) { let path = entry.path(); if path.is_dir() { rust_files(&path, out); } else if path.extension().is_some_and(|e| e == "rs") { out.push(path); } }}
/// The code, with the prose taken out.////// This tree documents itself heavily and names module paths in prose/// constantly — `crate::cmd::pr::write` appears in a dozen doc comments that/// are not imports. Comments are therefore dropped before anything is/// matched, or every rule below would fail on its own explanation.////// A `//` is only treated as a comment when it is not preceded by `:`, which/// keeps `at://` and `https://` inside string literals intact.fn code_only(text: &str) -> String { let mut out = String::with_capacity(text.len()); for line in text.lines() { if line.trim_start().starts_with("//") { continue; } let mut cut = line.len(); let bytes = line.as_bytes(); for i in 0..line.len().saturating_sub(1) { if bytes[i] == b'/' && bytes[i + 1] == b'/' && (i == 0 || bytes[i - 1] != b':') { cut = i; break; } } out.push_str(&line[..cut]); out.push('\n'); } out}
/// Every file under `src/<folder>` whose code mentions `needle`.fn offenders(folder: &str, needle: &str) -> Vec<String> { let mut files = Vec::new(); rust_files(&src().join(folder), &mut files); files.sort(); files .into_iter() .filter(|p| code_only(&std::fs::read_to_string(p).unwrap()).contains(needle)) .map(|p| { p.strip_prefix(src().parent().unwrap()) .unwrap_or(&p) .display() .to_string() }) .collect()}
/// **Rule 1.** `lexicon/` is the pure layer — syntax, record shapes,/// conventions. The moment it opens a socket it stops being testable without/// one, and every test of a *caller* becomes a test of the network.#[test]fn lexicon_imports_nothing_from_clients() { assert!( offenders("lexicon", "crate::clients").is_empty(), "lexicon/ must stay pure; found: {:?}", offenders("lexicon", "crate::clients") );}
/// **Rule 2.** A command is the top of the tree. Anything two commands both/// need belongs lower down, not somewhere to reach up for.////// No exceptions. There was one — `clients/atproto/oauth/pages.rs` reached/// into `cmd::about` for the field of DNA behind the post-login page — and it/// went away when the page and the field moved to `html/` and `art.rs`. This/// test carried it as a named allowlist entry that failed once it stopped/// being needed, which is how it came to be deleted rather than forgotten.#[test]fn nothing_outside_cmd_imports_cmd() { let found = ["clients", "config", "lexicon", "logging", "term", "html"] .into_iter() .flat_map(|folder| offenders(folder, "crate::cmd")) .collect::<Vec<_>>(); assert!(found.is_empty(), "these reach up into cmd/: {found:?}");}
/// **The `config/` constraint**, which is not one of the page's three rules/// and was labelled as its third until the real one landed below: no client/// asks `config/` who we are.////// A client takes the account, the DID or the session as an argument, from/// the command that already knows. `config::dir` is deliberately not covered:/// a path is not an identity, and `clients/git/ssh.rs` uses it to park a/// matched push key beside the session store.////// The difference is testability. A client handed a DID can be driven with/// any DID; a client that looks one up can only be driven by the machine it/// is running on.#[test]fn no_client_asks_config_who_we_are() { let found = offenders("clients", "crate::config::account"); assert!( found.is_empty(), "a client reached for the current account instead of being handed it: {found:?}" );}
/// **Rule 3**, which nothing checked until now. `clients/git/` owns every/// interaction with git: nothing else builds a `Command::new("git")`.////// It was a claim in two doc comments and true only by inspection, and it/// stopped being true at least once — `cmd/report.rs` ran `git --version`/// through a `Command` of its own for the version line in a bug report./// Harmless in itself, and exactly the crack the next one would have gone/// through: [`atgc logs git`](../src/logging/git.rs) records every git/// subprocess by sitting under the single `spawn` in `clients/git/run.rs`,/// so a caller that builds its own is a git process nothing records.////// `testutil` is the one exception the page names, and it is scaffolding/// rather than a caller: its repos are built for tests to run *against*, and/// routing them through the client under test would make the fixture depend/// on the thing being fixed.#[test]fn nothing_outside_clients_git_builds_a_git_command() { let mut files = Vec::new(); rust_files(&src(), &mut files); files.sort(); let found: Vec<String> = files .into_iter() .filter(|p| !p.starts_with(src().join("clients/git"))) .filter(|p| p.file_name().is_some_and(|n| n != "testutil.rs")) .filter(|p| { code_only(&std::fs::read_to_string(p).unwrap()).contains("Command::new(\"git\"") }) .map(|p| p.display().to_string()) .collect(); assert!( found.is_empty(), "these start git outside clients/git/, where nothing records it: {found:?}" );}
/// The comment stripper has to be right or every rule above passes for the/// wrong reason — a false negative here is silent.#[test]fn prose_is_stripped_but_code_and_urls_survive() { assert_eq!(code_only("//! see crate::cmd::pr\n").trim(), ""); assert_eq!(code_only("/// crate::cmd::pr\n").trim(), ""); assert_eq!(code_only(" // crate::cmd::pr\n").trim(), ""); assert_eq!( code_only("use crate::cmd::pr; // crate::cmd::stack\n").trim(), "use crate::cmd::pr;" ); // The one that would break it: `//` inside a URL is not a comment. assert_eq!( code_only("let uri = \"at://did:plc:abc/x\";\n").trim(), "let uri = \"at://did:plc:abc/x\";" );}