1//! Provides constructs for the Zed app version and release channel.
2
3#![deny(missing_docs)]
4
5use std::{env, str::FromStr};
6
7use gpui::{AppContext, Global, SemanticVersion};
8use once_cell::sync::Lazy;
9
10static RELEASE_CHANNEL_NAME: Lazy<String> = if cfg!(debug_assertions) {
11 Lazy::new(|| {
12 env::var("ZED_RELEASE_CHANNEL")
13 .unwrap_or_else(|_| include_str!("../../zed/RELEASE_CHANNEL").trim().to_string())
14 })
15} else {
16 Lazy::new(|| include_str!("../../zed/RELEASE_CHANNEL").trim().to_string())
17};
18
19#[doc(hidden)]
20pub static RELEASE_CHANNEL: Lazy<ReleaseChannel> =
21 Lazy::new(|| match ReleaseChannel::from_str(&RELEASE_CHANNEL_NAME) {
22 Ok(channel) => channel,
23 _ => panic!("invalid release channel {}", *RELEASE_CHANNEL_NAME),
24 });
25
26/// The Git commit SHA that Zed was built at.
27#[derive(Clone)]
28pub struct AppCommitSha(pub String);
29
30struct GlobalAppCommitSha(AppCommitSha);
31
32impl Global for GlobalAppCommitSha {}
33
34impl AppCommitSha {
35 /// Returns the global [`AppCommitSha`], if one is set.
36 pub fn try_global(cx: &AppContext) -> Option<AppCommitSha> {
37 cx.try_global::<GlobalAppCommitSha>()
38 .map(|sha| sha.0.clone())
39 }
40
41 /// Sets the global [`AppCommitSha`].
42 pub fn set_global(sha: AppCommitSha, cx: &mut AppContext) {
43 cx.set_global(GlobalAppCommitSha(sha))
44 }
45}
46
47struct GlobalAppVersion(SemanticVersion);
48
49impl Global for GlobalAppVersion {}
50
51/// The version of Zed.
52pub struct AppVersion;
53
54impl AppVersion {
55 /// Initializes the global [`AppVersion`].
56 ///
57 /// Attempts to read the version number from the following locations, in order:
58 /// 1. the `ZED_APP_VERSION` environment variable,
59 /// 2. the [`AppContext::app_metadata`],
60 /// 3. the passed in `pkg_version`.
61 pub fn init(pkg_version: &str, cx: &mut AppContext) {
62 let version = if let Ok(from_env) = env::var("ZED_APP_VERSION") {
63 from_env.parse().expect("invalid ZED_APP_VERSION")
64 } else {
65 cx.app_metadata()
66 .app_version
67 .unwrap_or_else(|| pkg_version.parse().expect("invalid version in Cargo.toml"))
68 };
69 cx.set_global(GlobalAppVersion(version))
70 }
71
72 /// Returns the global version number.
73 pub fn global(cx: &AppContext) -> SemanticVersion {
74 cx.global::<GlobalAppVersion>().0
75 }
76}
77
78/// A Zed release channel.
79#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
80pub enum ReleaseChannel {
81 /// The development release channel.
82 ///
83 /// Used for local debug builds of Zed.
84 #[default]
85 Dev,
86
87 /// The Nightly release channel.
88 Nightly,
89
90 /// The Preview release channel.
91 Preview,
92
93 /// The Stable release channel.
94 Stable,
95}
96
97struct GlobalReleaseChannel(ReleaseChannel);
98
99impl Global for GlobalReleaseChannel {}
100
101/// Initializes the release channel.
102pub fn init(pkg_version: &str, cx: &mut AppContext) {
103 AppVersion::init(pkg_version, cx);
104 cx.set_global(GlobalReleaseChannel(*RELEASE_CHANNEL))
105}
106
107impl ReleaseChannel {
108 /// Returns the global [`ReleaseChannel`].
109 pub fn global(cx: &AppContext) -> Self {
110 cx.global::<GlobalReleaseChannel>().0
111 }
112
113 /// Returns the global [`ReleaseChannel`], if one is set.
114 pub fn try_global(cx: &AppContext) -> Option<Self> {
115 cx.try_global::<GlobalReleaseChannel>()
116 .map(|channel| channel.0)
117 }
118
119 /// Returns the display name for this [`ReleaseChannel`].
120 pub fn display_name(&self) -> &'static str {
121 match self {
122 ReleaseChannel::Dev => "Zed Dev",
123 ReleaseChannel::Nightly => "Zed Nightly",
124 ReleaseChannel::Preview => "Zed Preview",
125 ReleaseChannel::Stable => "Zed",
126 }
127 }
128
129 /// Returns the programmatic name for this [`ReleaseChannel`].
130 pub fn dev_name(&self) -> &'static str {
131 match self {
132 ReleaseChannel::Dev => "dev",
133 ReleaseChannel::Nightly => "nightly",
134 ReleaseChannel::Preview => "preview",
135 ReleaseChannel::Stable => "stable",
136 }
137 }
138
139 /// Returns the query parameter for this [`ReleaseChannel`].
140 pub fn release_query_param(&self) -> Option<&'static str> {
141 match self {
142 Self::Dev => None,
143 Self::Nightly => Some("nightly=1"),
144 Self::Preview => Some("preview=1"),
145 Self::Stable => None,
146 }
147 }
148}
149
150/// Error indicating that release channel string does not match any known release channel names.
151#[derive(Copy, Clone, Debug, Hash, PartialEq)]
152pub struct InvalidReleaseChannel;
153
154impl FromStr for ReleaseChannel {
155 type Err = InvalidReleaseChannel;
156
157 fn from_str(channel: &str) -> Result<Self, Self::Err> {
158 Ok(match channel {
159 "dev" => ReleaseChannel::Dev,
160 "nightly" => ReleaseChannel::Nightly,
161 "preview" => ReleaseChannel::Preview,
162 "stable" => ReleaseChannel::Stable,
163 _ => return Err(InvalidReleaseChannel),
164 })
165 }
166}