1use std::future::Future;
4use std::pin::Pin;
5use std::time::Duration;
6
7use crate::exec::ExecError;
8
9pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum Concurrency {
20 Shared,
22 Exclusive,
24}
25
26#[derive(Debug, Clone)]
32pub struct Invocation {
33 pub args: Vec<String>,
34 pub concurrency: Concurrency,
35 pub timeout: Duration,
36 pub stdin: Option<Vec<u8>>,
39}
40
41impl Invocation {
42 pub fn read<I, S>(args: I) -> Self
44 where
45 I: IntoIterator<Item = S>,
46 S: Into<String>,
47 {
48 Self {
49 args: args.into_iter().map(Into::into).collect(),
50 concurrency: Concurrency::Shared,
51 timeout: crate::exec::DEFAULT_TIMEOUT,
52 stdin: None,
53 }
54 }
55
56 pub fn mutate<I, S>(args: I) -> Self
58 where
59 I: IntoIterator<Item = S>,
60 S: Into<String>,
61 {
62 Self {
63 concurrency: Concurrency::Exclusive,
64 ..Self::read(args)
65 }
66 }
67
68 pub fn mutate_shared<I, S>(args: I) -> Self
77 where
78 I: IntoIterator<Item = S>,
79 S: Into<String>,
80 {
81 Self::read(args)
82 }
83
84 #[must_use]
85 pub fn with_timeout(mut self, timeout: Duration) -> Self {
86 self.timeout = timeout;
87 self
88 }
89
90 #[must_use]
91 pub fn with_stdin(mut self, stdin: impl Into<Vec<u8>>) -> Self {
92 self.stdin = Some(stdin.into());
93 self
94 }
95
96 pub fn display(&self) -> String {
99 let mut out = String::from("tailscale");
100 for arg in &self.args {
101 out.push(' ');
102 out.push_str(arg);
103 }
104 out
105 }
106}
107
108#[derive(Debug, Clone, PartialEq, Eq)]
110pub struct Output {
111 pub exit_code: Option<i32>,
113 pub stdout: Vec<u8>,
114 pub stderr: String,
115}
116
117impl Output {
118 pub fn success(&self) -> bool {
119 self.exit_code == Some(0)
120 }
121
122 pub fn stdout_str(&self) -> std::borrow::Cow<'_, str> {
123 String::from_utf8_lossy(&self.stdout)
124 }
125}
126
127pub trait LocalBackend: Send + Sync + std::fmt::Debug {
132 fn run<'a>(&'a self, invocation: Invocation) -> BoxFuture<'a, Result<Output, ExecError>>;
133}
134
135impl<T: LocalBackend + ?Sized> LocalBackend for std::sync::Arc<T> {
136 fn run<'a>(&'a self, invocation: Invocation) -> BoxFuture<'a, Result<Output, ExecError>> {
137 (**self).run(invocation)
138 }
139}
140
141#[derive(Debug, Clone)]
148pub struct Unavailable {
149 reason: String,
150}
151
152impl Unavailable {
153 pub fn new(reason: impl Into<String>) -> Self {
154 Self {
155 reason: reason.into(),
156 }
157 }
158}
159
160impl Default for Unavailable {
161 fn default() -> Self {
162 Self::new("the local surface is not available")
163 }
164}
165
166impl LocalBackend for Unavailable {
167 fn run<'a>(&'a self, _invocation: Invocation) -> BoxFuture<'a, Result<Output, ExecError>> {
168 Box::pin(async move {
169 Err(ExecError::BinaryNotFound {
170 searched: vec![self.reason.clone()],
171 })
172 })
173 }
174}