Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
71 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

The sidebar asks billing whether it is on, not for the account, so no page waits on the card processor1//! Calling outside systems. Every answer comes back as a status and a body,
2//! so each provider decides what a refusal means.
3
4use serde_json::Value;
5use worker::{Fetch, Headers, Method, Request, RequestInit, Result};
6
7pub struct Answer {
8 pub status: u16,
9 pub body: String,
10}
11
12impl Answer {
13 pub fn ok(&self) -> bool {
14 (200..300).contains(&self.status)
15 }
16
17 pub fn json(&self) -> Value {
18 serde_json::from_str(&self.body).unwrap_or(Value::Null)
19 }
20
21 /// What went wrong, briefly, for a person to read.
22 pub fn problem(&self, system: &str) -> String {
23 let said = self.json();
24 let message = said["message"]
25 .as_str()
26 .or_else(|| said["detail"].as_str())
27 .or_else(|| said["error"]["message"].as_str())
28 .or_else(|| said["errorMessages"][0].as_str())
29 .or_else(|| said["errors"][0]["message"].as_str())
30 .map(str::to_owned)
31 .unwrap_or_else(|| self.body.chars().take(200).collect());
32 match self.status {
33 401 => format!("{system} did not accept the credentials ({message})."),
34 403 => format!("{system} refused: the credentials cannot do this ({message})."),
35 404 => format!("{system} has nothing there ({message})."),
36 status => format!("{system} answered {status}: {message}"),
37 }
38 }
39}
40
41pub async fn send(method: Method, url: &str, headers: &[(&str, &str)], body: Option<String>) -> Result<Answer> {
42 let sent = Headers::new();
43 sent.set("user-agent", "g1t (+https://g1t.sh)")?;
44 sent.set("accept", "application/json")?;
45 for (name, value) in headers {
46 sent.set(name, value)?;
47 }
48 if body.is_some() && !headers.iter().any(|(name, _)| name.eq_ignore_ascii_case("content-type")) {
49 sent.set("content-type", "application/json")?;
50 }
51 let mut init = RequestInit::new();
52 init.with_method(method).with_headers(sent);
53 if let Some(body) = body {
54 init.with_body(Some(body.into()));
55 }
56 let mut response = Fetch::Request(Request::new_with_init(url, &init)?).send().await?;
57 Ok(Answer {
58 status: response.status_code(),
59 body: response.text().await.unwrap_or_default(),
60 })
61}
62
63/// `text`, cut to `max` characters at a line break where one is near.
64pub fn shorten(text: &str, max: usize) -> String {
65 if text.chars().count() <= max {
66 return text.to_owned();
67 }
68 let cut: String = text.chars().take(max).collect();
69 let at = cut.rfind('\n').filter(|at| *at > max / 2).unwrap_or(cut.len());
70 format!("{}\n\n…", cut[..at].trim_end())
71}

This file's history is long; its oldest lines are credited to the oldest commit read.