Extract SemanticVersion into its own crate (#9956)

This PR extracts the `SemanticVersion` out of `util` and into its own
`SemanticVersion` crate.

This allows for making use of `SemanticVersion` without needing to pull
in some of the heavier dependencies included in the `util` crate.

As part of this the public API for `SemanticVersion` has been tidied up
a bit.

Release Notes:

- N/A
This commit is contained in:
Marshall Bowers 2024-03-29 12:11:57 -04:00 committed by GitHub
parent 77f1cc95b8
commit 16e6f5643c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 147 additions and 110 deletions

14
Cargo.lock generated
View File

@ -2265,6 +2265,7 @@ dependencies = [
"rustc-demangle",
"scrypt",
"sea-orm",
"semantic_version",
"semver",
"serde",
"serde_derive",
@ -3558,6 +3559,7 @@ dependencies = [
"parking_lot",
"project",
"schemars",
"semantic_version",
"serde",
"serde_json",
"serde_json_lenient",
@ -3610,6 +3612,7 @@ dependencies = [
"language",
"picker",
"project",
"semantic_version",
"serde",
"settings",
"smallvec",
@ -4446,6 +4449,7 @@ dependencies = [
"resvg",
"schemars",
"seahash",
"semantic_version",
"serde",
"serde_derive",
"serde_json",
@ -8496,6 +8500,14 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba"
[[package]]
name = "semantic_version"
version = "0.1.0"
dependencies = [
"anyhow",
"serde",
]
[[package]]
name = "semver"
version = "1.0.18"
@ -9659,8 +9671,8 @@ dependencies = [
name = "telemetry_events"
version = "0.1.0"
dependencies = [
"semantic_version",
"serde",
"util",
]
[[package]]

View File

@ -70,6 +70,7 @@ members = [
"crates/task",
"crates/tasks_ui",
"crates/search",
"crates/semantic_version",
"crates/settings",
"crates/snippet",
"crates/sqlez",
@ -184,6 +185,7 @@ rpc = { path = "crates/rpc" }
task = { path = "crates/task" }
tasks_ui = { path = "crates/tasks_ui" }
search = { path = "crates/search" }
semantic_version = { path = "crates/semantic_version" }
settings = { path = "crates/settings" }
snippet = { path = "crates/snippet" }
sqlez = { path = "crates/sqlez" }

View File

@ -46,6 +46,7 @@ reqwest = { version = "0.11", features = ["json"] }
rpc.workspace = true
scrypt = "0.7"
sea-orm = { version = "0.12.x", features = ["sqlx-postgres", "postgres-array", "runtime-tokio-rustls", "with-uuid"] }
semantic_version.workspace = true
semver.workspace = true
serde.workspace = true
serde_derive.workspace = true

View File

@ -10,6 +10,7 @@ use axum::{
Extension, Router, TypedHeader,
};
use rpc::ExtensionMetadata;
use semantic_version::SemanticVersion;
use serde::{Serialize, Serializer};
use sha2::{Digest, Sha256};
use std::sync::{Arc, OnceLock};
@ -17,7 +18,6 @@ use telemetry_events::{
ActionEvent, AppEvent, AssistantEvent, CallEvent, CopilotEvent, CpuEvent, EditEvent,
EditorEvent, Event, EventRequestBody, EventWrapper, ExtensionEvent, MemoryEvent, SettingEvent,
};
use util::SemanticVersion;
pub fn router() -> Router {
Router::new()
@ -528,9 +528,9 @@ impl EditorEventRow {
Self {
app_version: body.app_version.clone(),
major: semver.map(|s| s.major as i32),
minor: semver.map(|s| s.minor as i32),
patch: semver.map(|s| s.patch as i32),
major: semver.map(|v| v.major() as i32),
minor: semver.map(|v| v.minor() as i32),
patch: semver.map(|v| v.patch() as i32),
release_channel: body.release_channel.clone().unwrap_or_default(),
os_name: body.os_name.clone(),
os_version: body.os_version.clone().unwrap_or_default(),
@ -590,9 +590,9 @@ impl CopilotEventRow {
Self {
app_version: body.app_version.clone(),
major: semver.map(|s| s.major as i32),
minor: semver.map(|s| s.minor as i32),
patch: semver.map(|s| s.patch as i32),
major: semver.map(|v| v.major() as i32),
minor: semver.map(|v| v.minor() as i32),
patch: semver.map(|v| v.patch() as i32),
release_channel: body.release_channel.clone().unwrap_or_default(),
os_name: body.os_name.clone(),
os_version: body.os_version.clone().unwrap_or_default(),
@ -645,9 +645,9 @@ impl CallEventRow {
Self {
app_version: body.app_version.clone(),
major: semver.map(|s| s.major as i32),
minor: semver.map(|s| s.minor as i32),
patch: semver.map(|s| s.patch as i32),
major: semver.map(|v| v.major() as i32),
minor: semver.map(|v| v.minor() as i32),
patch: semver.map(|v| v.patch() as i32),
release_channel: body.release_channel.clone().unwrap_or_default(),
installation_id: body.installation_id.clone().unwrap_or_default(),
session_id: body.session_id.clone(),
@ -694,9 +694,9 @@ impl AssistantEventRow {
Self {
app_version: body.app_version.clone(),
major: semver.map(|s| s.major as i32),
minor: semver.map(|s| s.minor as i32),
patch: semver.map(|s| s.patch as i32),
major: semver.map(|v| v.major() as i32),
minor: semver.map(|v| v.minor() as i32),
patch: semver.map(|v| v.patch() as i32),
release_channel: body.release_channel.clone().unwrap_or_default(),
installation_id: body.installation_id.clone(),
session_id: body.session_id.clone(),
@ -738,9 +738,9 @@ impl CpuEventRow {
Self {
app_version: body.app_version.clone(),
major: semver.map(|s| s.major as i32),
minor: semver.map(|s| s.minor as i32),
patch: semver.map(|s| s.patch as i32),
major: semver.map(|v| v.major() as i32),
minor: semver.map(|v| v.minor() as i32),
patch: semver.map(|v| v.patch() as i32),
release_channel: body.release_channel.clone().unwrap_or_default(),
installation_id: body.installation_id.clone(),
session_id: body.session_id.clone(),
@ -785,9 +785,9 @@ impl MemoryEventRow {
Self {
app_version: body.app_version.clone(),
major: semver.map(|s| s.major as i32),
minor: semver.map(|s| s.minor as i32),
patch: semver.map(|s| s.patch as i32),
major: semver.map(|v| v.major() as i32),
minor: semver.map(|v| v.minor() as i32),
patch: semver.map(|v| v.patch() as i32),
release_channel: body.release_channel.clone().unwrap_or_default(),
installation_id: body.installation_id.clone(),
session_id: body.session_id.clone(),
@ -831,9 +831,9 @@ impl AppEventRow {
Self {
app_version: body.app_version.clone(),
major: semver.map(|s| s.major as i32),
minor: semver.map(|s| s.minor as i32),
patch: semver.map(|s| s.patch as i32),
major: semver.map(|v| v.major() as i32),
minor: semver.map(|v| v.minor() as i32),
patch: semver.map(|v| v.patch() as i32),
release_channel: body.release_channel.clone().unwrap_or_default(),
installation_id: body.installation_id.clone(),
session_id: body.session_id.clone(),
@ -876,9 +876,9 @@ impl SettingEventRow {
Self {
app_version: body.app_version.clone(),
major: semver.map(|s| s.major as i32),
minor: semver.map(|s| s.minor as i32),
patch: semver.map(|s| s.patch as i32),
major: semver.map(|v| v.major() as i32),
minor: semver.map(|v| v.minor() as i32),
patch: semver.map(|v| v.patch() as i32),
release_channel: body.release_channel.clone().unwrap_or_default(),
installation_id: body.installation_id.clone(),
session_id: body.session_id.clone(),
@ -927,9 +927,9 @@ impl ExtensionEventRow {
Self {
app_version: body.app_version.clone(),
major: semver.map(|s| s.major as i32),
minor: semver.map(|s| s.minor as i32),
patch: semver.map(|s| s.patch as i32),
major: semver.map(|v| v.major() as i32),
minor: semver.map(|v| v.minor() as i32),
patch: semver.map(|v| v.patch() as i32),
release_channel: body.release_channel.clone().unwrap_or_default(),
installation_id: body.installation_id.clone(),
session_id: body.session_id.clone(),
@ -991,9 +991,9 @@ impl EditEventRow {
Self {
app_version: body.app_version.clone(),
major: semver.map(|s| s.major as i32),
minor: semver.map(|s| s.minor as i32),
patch: semver.map(|s| s.patch as i32),
major: semver.map(|v| v.major() as i32),
minor: semver.map(|v| v.minor() as i32),
patch: semver.map(|v| v.patch() as i32),
release_channel: body.release_channel.clone().unwrap_or_default(),
installation_id: body.installation_id.clone(),
session_id: body.session_id.clone(),
@ -1040,9 +1040,9 @@ impl ActionEventRow {
Self {
app_version: body.app_version.clone(),
major: semver.map(|s| s.major as i32),
minor: semver.map(|s| s.minor as i32),
patch: semver.map(|s| s.patch as i32),
major: semver.map(|v| v.major() as i32),
minor: semver.map(|v| v.minor() as i32),
patch: semver.map(|v| v.patch() as i32),
release_channel: body.release_channel.clone().unwrap_or_default(),
installation_id: body.installation_id.clone(),
session_id: body.session_id.clone(),

View File

@ -1,9 +1,8 @@
use collections::HashMap;
use serde_derive::Deserialize;
use serde_derive::Serialize;
use semantic_version::SemanticVersion;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use util::SemanticVersion;
#[derive(Debug)]
pub struct IpsFile {

View File

@ -46,6 +46,7 @@ use rpc::{
},
Connection, ConnectionId, ErrorCode, ErrorCodeExt, ErrorExt, Peer, Receipt, TypedEnvelope,
};
use semantic_version::SemanticVersion;
use serde::{Serialize, Serializer};
use std::{
any::TypeId,
@ -68,7 +69,7 @@ use tracing::{
field::{self},
info_span, instrument, Instrument,
};
use util::{http::IsahcHttpClient, SemanticVersion};
use util::http::IsahcHttpClient;
pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30);

View File

@ -2,9 +2,10 @@ use crate::db::{ChannelId, ChannelRole, UserId};
use anyhow::{anyhow, Result};
use collections::{BTreeMap, HashMap, HashSet};
use rpc::ConnectionId;
use semantic_version::SemanticVersion;
use serde::Serialize;
use std::fmt;
use tracing::instrument;
use util::{semver, SemanticVersion};
#[derive(Default, Serialize)]
pub struct ConnectionPool {
@ -20,7 +21,6 @@ struct ConnectedUser {
#[derive(Debug, Serialize)]
pub struct ZedVersion(pub SemanticVersion);
use std::fmt;
impl fmt::Display for ZedVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -30,7 +30,7 @@ impl fmt::Display for ZedVersion {
impl ZedVersion {
pub fn can_collaborate(&self) -> bool {
self.0 >= semver(0, 127, 3)
self.0 >= SemanticVersion::new(0, 127, 3)
}
}

View File

@ -19,7 +19,6 @@ use futures::{channel::oneshot, StreamExt as _};
use gpui::{BackgroundExecutor, Context, Model, Task, TestAppContext, View, VisualTestContext};
use language::LanguageRegistry;
use node_runtime::FakeNodeRuntime;
use notifications::NotificationStore;
use parking_lot::Mutex;
use project::{Project, WorktreeId};
@ -27,6 +26,7 @@ use rpc::{
proto::{self, ChannelRole},
RECEIVE_TIMEOUT,
};
use semantic_version::SemanticVersion;
use serde_json::json;
use settings::SettingsStore;
use std::{
@ -39,7 +39,7 @@ use std::{
Arc,
},
};
use util::{http::FakeHttpClient, SemanticVersion};
use util::http::FakeHttpClient;
use workspace::{Workspace, WorkspaceId, WorkspaceStore};
pub struct TestServer {

View File

@ -33,6 +33,7 @@ lsp.workspace = true
node_runtime.workspace = true
project.workspace = true
schemars.workspace = true
semantic_version.workspace = true
serde.workspace = true
serde_json.workspace = true
settings.workspace = true

View File

@ -2,6 +2,7 @@ use anyhow::{anyhow, Context, Result};
use collections::BTreeMap;
use fs::Fs;
use language::LanguageServerName;
use semantic_version::SemanticVersion;
use serde::{Deserialize, Serialize};
use std::{
ffi::OsStr,
@ -9,7 +10,6 @@ use std::{
path::{Path, PathBuf},
sync::Arc,
};
use util::SemanticVersion;
/// This is the old version of the extension manifest, from when it was `extension.json`.
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]

View File

@ -33,6 +33,7 @@ use language::{
QUERY_FILENAME_PREFIXES,
};
use node_runtime::NodeRuntime;
use semantic_version::SemanticVersion;
use serde::{Deserialize, Serialize};
use settings::Settings;
use std::str::FromStr;
@ -44,7 +45,6 @@ use std::{
};
use theme::{ThemeRegistry, ThemeSettings};
use url::Url;
use util::SemanticVersion;
use util::{
http::{AsyncBody, HttpClient, HttpClientWithUrl},
maybe,

View File

@ -14,11 +14,12 @@ use futures::{
use gpui::BackgroundExecutor;
use language::LanguageRegistry;
use node_runtime::NodeRuntime;
use semantic_version::SemanticVersion;
use std::{
path::{Path, PathBuf},
sync::{Arc, OnceLock},
};
use util::{http::HttpClient, SemanticVersion};
use util::http::HttpClient;
use wasmtime::{
component::{Component, ResourceTable},
Engine, Store,
@ -203,11 +204,11 @@ pub fn parse_wasm_extension_version(
fn parse_wasm_extension_version_custom_section(data: &[u8]) -> Option<SemanticVersion> {
if data.len() == 6 {
Some(SemanticVersion {
major: u16::from_be_bytes([data[0], data[1]]) as _,
minor: u16::from_be_bytes([data[2], data[3]]) as _,
patch: u16::from_be_bytes([data[4], data[5]]) as _,
})
Some(SemanticVersion::new(
u16::from_be_bytes([data[0], data[1]]) as _,
u16::from_be_bytes([data[2], data[3]]) as _,
u16::from_be_bytes([data[4], data[5]]) as _,
))
} else {
None
}

View File

@ -4,8 +4,8 @@ mod v0_0_4;
use super::{wasm_engine, WasmState};
use anyhow::{Context, Result};
use language::LspAdapterDelegate;
use semantic_version::SemanticVersion;
use std::sync::Arc;
use util::SemanticVersion;
use wasmtime::{
component::{Component, Instance, Linker, Resource},
Store,

View File

@ -3,15 +3,11 @@ use crate::wasm_host::WasmState;
use anyhow::Result;
use async_trait::async_trait;
use language::{LanguageServerBinaryStatus, LspAdapterDelegate};
use semantic_version::SemanticVersion;
use std::sync::{Arc, OnceLock};
use util::SemanticVersion;
use wasmtime::component::{Linker, Resource};
pub const VERSION: SemanticVersion = SemanticVersion {
major: 0,
minor: 0,
patch: 1,
};
pub const VERSION: SemanticVersion = SemanticVersion::new(0, 0, 1);
wasmtime::component::bindgen!({
async: true,

View File

@ -5,19 +5,16 @@ use async_tar::Archive;
use async_trait::async_trait;
use futures::io::BufReader;
use language::{LanguageServerBinaryStatus, LspAdapterDelegate};
use semantic_version::SemanticVersion;
use std::{
env,
path::PathBuf,
sync::{Arc, OnceLock},
};
use util::{maybe, SemanticVersion};
use util::maybe;
use wasmtime::component::{Linker, Resource};
pub const VERSION: SemanticVersion = SemanticVersion {
major: 0,
minor: 0,
patch: 4,
};
pub const VERSION: SemanticVersion = SemanticVersion::new(0, 0, 4);
wasmtime::component::bindgen!({
async: true,

View File

@ -26,6 +26,7 @@ gpui.workspace = true
language.workspace = true
picker.workspace = true
project.workspace = true
semantic_version.workspace = true
serde.workspace = true
settings.workspace = true
smallvec.workspace = true

View File

@ -9,9 +9,10 @@ use gpui::{
prelude::*, AppContext, DismissEvent, EventEmitter, FocusableView, Task, View, WeakView,
};
use picker::{Picker, PickerDelegate};
use semantic_version::SemanticVersion;
use settings::update_settings_file;
use ui::{prelude::*, HighlightedLabel, ListItem, ListItemSpacing};
use util::{ResultExt, SemanticVersion};
use util::ResultExt;
use workspace::ModalView;
pub struct ExtensionVersionSelector {

View File

@ -57,6 +57,7 @@ refineable.workspace = true
resvg = "0.40"
schemars.workspace = true
seahash = "4.1"
semantic_version.workspace = true
serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true

View File

@ -53,10 +53,10 @@ pub use keystroke::*;
pub(crate) use linux::*;
#[cfg(target_os = "macos")]
pub(crate) use mac::*;
pub use semantic_version::SemanticVersion;
#[cfg(any(test, feature = "test-support"))]
pub(crate) use test::*;
use time::UtcOffset;
pub use util::SemanticVersion;
#[cfg(target_os = "windows")]
pub(crate) use windows::*;

View File

@ -353,19 +353,11 @@ impl Platform for LinuxPlatform {
}
fn os_version(&self) -> Result<SemanticVersion> {
Ok(SemanticVersion {
major: 1,
minor: 0,
patch: 0,
})
Ok(SemanticVersion::new(1, 0, 0))
}
fn app_version(&self) -> Result<SemanticVersion> {
Ok(SemanticVersion {
major: 1,
minor: 0,
patch: 0,
})
Ok(SemanticVersion::new(1, 0, 0))
}
fn app_path(&self) -> Result<PathBuf> {

View File

@ -718,11 +718,11 @@ impl Platform for MacPlatform {
unsafe {
let process_info = NSProcessInfo::processInfo(nil);
let version = process_info.operatingSystemVersion();
Ok(SemanticVersion {
major: version.majorVersion as usize,
minor: version.minorVersion as usize,
patch: version.patchVersion as usize,
})
Ok(SemanticVersion::new(
version.majorVersion as usize,
version.minorVersion as usize,
version.patchVersion as usize,
))
}
}

View File

@ -12,13 +12,14 @@ use std::{
sync::{Arc, OnceLock},
};
use ::util::{ResultExt, SemanticVersion};
use ::util::ResultExt;
use anyhow::{anyhow, Context, Result};
use async_task::Runnable;
use copypasta::{ClipboardContext, ClipboardProvider};
use futures::channel::oneshot::{self, Receiver};
use itertools::Itertools;
use parking_lot::{Mutex, RwLock};
use semantic_version::SemanticVersion;
use smallvec::SmallVec;
use time::UtcOffset;
use windows::{
@ -513,11 +514,11 @@ impl Platform for WindowsPlatform {
let mut info = unsafe { std::mem::zeroed() };
let status = unsafe { RtlGetVersion(&mut info) };
if status.is_ok() {
Ok(SemanticVersion {
major: info.dwMajorVersion as _,
minor: info.dwMinorVersion as _,
patch: info.dwBuildNumber as _,
})
Ok(SemanticVersion::new(
info.dwMajorVersion as _,
info.dwMinorVersion as _,
info.dwBuildNumber as _,
))
} else {
Err(anyhow::anyhow!(
"unable to get Windows version: {}",
@ -606,11 +607,11 @@ impl Platform for WindowsPlatform {
let version_info = unsafe { &*(version_info_raw as *mut VS_FIXEDFILEINFO) };
// https://learn.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo
if version_info.dwSignature == 0xFEEF04BD {
return Ok(SemanticVersion {
major: ((version_info.dwProductVersionMS >> 16) & 0xFFFF) as usize,
minor: (version_info.dwProductVersionMS & 0xFFFF) as usize,
patch: ((version_info.dwProductVersionLS >> 16) & 0xFFFF) as usize,
});
return Ok(SemanticVersion::new(
((version_info.dwProductVersionMS >> 16) & 0xFFFF) as usize,
(version_info.dwProductVersionMS & 0xFFFF) as usize,
((version_info.dwProductVersionLS >> 16) & 0xFFFF) as usize,
));
} else {
log::error!(
"no version info present: {}",

View File

@ -0,0 +1,16 @@
[package]
name = "semantic_version"
version = "0.1.0"
edition = "2021"
publish = false
license = "Apache-2.0"
[lints]
workspace = true
[lib]
path = "src/semantic_version.rs"
[dependencies]
anyhow.workspace = true
serde.workspace = true

View File

@ -0,0 +1 @@
../../LICENSE-APACHE

View File

@ -1,3 +1,7 @@
//! Constructs for working with [semantic versions](https://semver.org/).
#![deny(missing_docs)]
use std::{
fmt::{self, Display},
str::FromStr,
@ -6,34 +10,46 @@ use std::{
use anyhow::{anyhow, Result};
use serde::{de::Error, Deserialize, Serialize};
/// A datastructure representing a semantic version number
/// A [semantic version](https://semver.org/) number.
#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
pub struct SemanticVersion {
pub major: usize,
pub minor: usize,
pub patch: usize,
}
pub fn semver(major: usize, minor: usize, patch: usize) -> SemanticVersion {
SemanticVersion {
major,
minor,
patch,
}
major: usize,
minor: usize,
patch: usize,
}
impl SemanticVersion {
pub fn new(major: usize, minor: usize, patch: usize) -> Self {
/// Returns a new [`SemanticVersion`] from the given components.
pub const fn new(major: usize, minor: usize, patch: usize) -> Self {
Self {
major,
minor,
patch,
}
}
/// Returns the major version number.
#[inline(always)]
pub fn major(&self) -> usize {
self.major
}
/// Returns the minor version number.
#[inline(always)]
pub fn minor(&self) -> usize {
self.minor
}
/// Returns the patch version number.
#[inline(always)]
pub fn patch(&self) -> usize {
self.patch
}
}
impl FromStr for SemanticVersion {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self> {
let mut components = s.trim().split('.');
let major = components

View File

@ -12,5 +12,5 @@ workspace = true
path = "src/telemetry_events.rs"
[dependencies]
semantic_version.workspace = true
serde.workspace = true
util.workspace = true

View File

@ -1,6 +1,6 @@
use semantic_version::SemanticVersion;
use serde::{Deserialize, Serialize};
use std::{fmt::Display, sync::Arc};
use util::SemanticVersion;
#[derive(Serialize, Deserialize, Debug)]
pub struct EventRequestBody {

View File

@ -3,14 +3,12 @@ pub mod fs;
pub mod github;
pub mod http;
pub mod paths;
mod semantic_version;
#[cfg(any(test, feature = "test-support"))]
pub mod test;
use futures::Future;
use lazy_static::lazy_static;
use rand::{seq::SliceRandom, Rng};
pub use semantic_version::*;
use std::{
borrow::Cow,
cmp::{self, Ordering},