cuttlefish_core/endpoint.rs
1//! Where the daemon listens, and the client connects.
2//!
3//! This lives in `cuttlefish-core` because it is the one fact the daemon and
4//! the CLI must agree on exactly, and they share no other crate — the CLI does
5//! not depend on `cuttlefishd` (that would pull axum and the whole server
6//! stack into a client). Restating the default in both places is precisely the
7//! kind of thing that drifts.
8
9use std::path::PathBuf;
10
11/// The daemon's default endpoint for this platform.
12///
13/// Two different kinds of name wearing one type. On unix this is a filesystem
14/// path and the socket is a real file; on Windows it is a named-pipe name,
15/// which lives in the pipe namespace and is not a file at all. `PathBuf`
16/// carries both because that is what the OS APIs take — do not infer that a
17/// pipe name has a parent directory to create, or that it can be `remove_file`d.
18pub fn default_endpoint() -> PathBuf {
19 #[cfg(unix)]
20 {
21 PathBuf::from("/tmp/cuttlefish.sock")
22 }
23 #[cfg(windows)]
24 {
25 PathBuf::from(r"\\.\pipe\cuttlefish")
26 }
27}
28
29#[cfg(test)]
30mod tests {
31 use super::*;
32
33 /// The daemon and the client each call this independently; if it ever
34 /// returned something empty or relative, they would still agree with each
35 /// other while both failing to connect.
36 #[test]
37 fn the_default_endpoint_is_absolute_and_non_empty() {
38 let endpoint = default_endpoint();
39 assert!(!endpoint.as_os_str().is_empty());
40
41 let shown = endpoint.display().to_string();
42 if cfg!(windows) {
43 assert!(
44 shown.starts_with(r"\\.\pipe\"),
45 "a Windows endpoint must name the pipe namespace: {shown}"
46 );
47 } else {
48 assert!(
49 endpoint.is_absolute(),
50 "a unix endpoint must not depend on the working directory: {shown}"
51 );
52 }
53 }
54}