tinycongress_api/
build_info.rs1use async_graphql::SimpleObject;
2use chrono::{DateTime, Utc};
3use serde::Serialize;
4use std::env;
5use utoipa::ToSchema;
6
7#[derive(Clone, Debug, PartialEq, Eq, Serialize, SimpleObject, ToSchema)]
9#[graphql(rename_fields = "camelCase")]
10#[serde(rename_all = "camelCase")]
11pub struct BuildInfo {
12 pub version: String,
13 pub git_sha: String,
14 pub build_time: String,
15 #[serde(skip_serializing_if = "Option::is_none")]
16 pub message: Option<String>,
17}
18
19#[derive(Clone, Debug)]
20pub struct BuildInfoProvider {
21 info: BuildInfo,
22}
23
24impl BuildInfoProvider {
25 #[must_use]
27 pub fn from_env() -> Self {
28 Self::from_lookup(|key| env::var(key).ok())
29 }
30
31 #[must_use]
33 pub fn from_lookup<F>(mut lookup: F) -> Self
34 where
35 F: FnMut(&str) -> Option<String>,
36 {
37 let version = lookup("APP_VERSION")
38 .or_else(|| lookup("VERSION"))
39 .unwrap_or_else(|| "dev".to_string());
40
41 let git_sha = lookup("GIT_SHA").unwrap_or_else(|| "unknown".to_string());
42
43 let build_time = lookup("BUILD_TIME")
44 .and_then(|value| normalize_build_time(&value))
45 .unwrap_or_else(|| "unknown".to_string());
46
47 let message = lookup("BUILD_MESSAGE");
48
49 let info = BuildInfo {
50 version,
51 git_sha,
52 build_time,
53 message,
54 };
55
56 Self { info }
57 }
58
59 #[must_use]
61 pub fn build_info(&self) -> BuildInfo {
62 self.info.clone()
63 }
64}
65
66fn normalize_build_time(value: &str) -> Option<String> {
67 DateTime::parse_from_rfc3339(value)
68 .or_else(|_| DateTime::parse_from_rfc3339(&format!("{value}Z")))
69 .map(|dt| dt.with_timezone(&Utc).to_rfc3339())
70 .ok()
71}