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 application ID that's used by Wayland as application ID
140 /// and WM_CLASS on X11.
141 /// This also has to match the bundle identifier for Zed on macOS.
142 pub fn app_id(&self) -> &'static str {
143 match self {
144 ReleaseChannel::Dev => "dev.zed.Zed-Dev",
145 ReleaseChannel::Nightly => "dev.zed.Zed-Nightly",
146 ReleaseChannel::Preview => "dev.zed.Zed-Preview",
147 ReleaseChannel::Stable => "dev.zed.Zed",
148 }
149 }
150
151 /// Returns the query parameter for this [`ReleaseChannel`].
152 pub fn release_query_param(&self) -> Option<&'static str> {
153 match self {
154 Self::Dev => None,
155 Self::Nightly => Some("nightly=1"),
156 Self::Preview => Some("preview=1"),
157 Self::Stable => None,
158 }
159 }
160}
161
162/// Error indicating that release channel string does not match any known release channel names.
163#[derive(Copy, Clone, Debug, Hash, PartialEq)]
164pub struct InvalidReleaseChannel;
165
166impl FromStr for ReleaseChannel {
167 type Err = InvalidReleaseChannel;
168
169 fn from_str(channel: &str) -> Result<Self, Self::Err> {
170 Ok(match channel {
171 "dev" => ReleaseChannel::Dev,
172 "nightly" => ReleaseChannel::Nightly,
173 "preview" => ReleaseChannel::Preview,
174 "stable" => ReleaseChannel::Stable,
175 _ => return Err(InvalidReleaseChannel),
176 })
177 }
178}