Files
cursor-api/build_info.rs
wisdgod 65a390d4f2 0.4.0-pre.14
This is a special version (since the repository hasn't been updated for a while). It includes partial updates from 0.3 to 0.4, along with several fixes for 0.4.0-pre.13.

这是一个特殊版本(因为一段时间没有更新存储库),它包含0.3至0.4的部分更新以及对0.4.0-pre.13的几处修复。
2025-12-23 11:18:28 +08:00

242 lines
7.8 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
include!("src/app/model/version.rs");
/// 版本字符串解析错误
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ParseError {
/// 整体格式错误(如缺少必需部分)
InvalidFormat,
/// 数字解析失败
InvalidNumber,
/// pre 部分格式错误
InvalidPreRelease,
/// build 部分格式错误
InvalidBuild,
// /// 正式版不能带 build 标识
// BuildWithoutPreview,
}
impl core::fmt::Display for ParseError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
ParseError::InvalidFormat => write!(f, "invalid version format"),
ParseError::InvalidNumber => write!(f, "invalid number in version"),
ParseError::InvalidPreRelease => write!(f, "invalid pre-release format"),
ParseError::InvalidBuild => write!(f, "invalid build format"),
// ParseError::BuildWithoutPreview => {
// write!(f, "build metadata cannot exist without pre-release version")
// }
}
}
}
impl std::error::Error for ParseError {}
impl core::str::FromStr for Version {
type Err = ParseError;
fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
// 按 '-' 分割基础版本号和扩展部分
let (base, extension) = match s.split_once('-') {
Some((base, ext)) => (base, Some(ext)),
None => (s, None),
};
// 解析基础版本号 major.minor.patch
let mut parts: [u16; 3] = [0, 0, 0];
let mut parsed_count = 0;
for (i, s) in base.split('.').enumerate() {
if i >= parts.len() {
return Err(ParseError::InvalidFormat);
}
parts[i] = s.parse().map_err(|_| ParseError::InvalidNumber)?;
parsed_count += 1;
}
if parsed_count != 3 {
return Err(ParseError::InvalidFormat);
}
let major = parts[0];
let minor = parts[1];
let patch = parts[2];
// 解析扩展部分(如果存在)
let stage =
if let Some(ext) = extension { parse_extension(ext)? } else { ReleaseStage::Release };
Ok(Version { major, minor, patch, stage })
}
}
/// 解析扩展部分pre.X 或 pre.X+build.Y
fn parse_extension(s: &str) -> core::result::Result<ReleaseStage, ParseError> {
// 检查是否以 "pre." 开头
if !s.starts_with("pre.") {
return Err(ParseError::InvalidPreRelease);
}
// 移除 "pre." 前缀
let after_pre = &s[4..];
// 按 '+' 分割 version 和 build 部分
let (version_str, build_str) = match after_pre.split_once('+') {
Some((ver, build_part)) => (ver, Some(build_part)),
None => (after_pre, None),
};
// 解析 pre 版本号
let version = version_str.parse().map_err(|_| ParseError::InvalidPreRelease)?;
// 解析 build 号(如果存在)
let build = if let Some(build_part) = build_str {
// 检查格式是否为 "build.X"
if !build_part.starts_with("build.") {
return Err(ParseError::InvalidBuild);
}
let build_num_str = &build_part[6..];
let build_num = build_num_str.parse().map_err(|_| ParseError::InvalidBuild)?;
Some(build_num)
} else {
None
};
Ok(ReleaseStage::Preview { version, build })
}
/**
* 更新版本号函数
* 此函数会读取 VERSION 文件中的数字将其加1然后保存回文件
* 如果 VERSION 文件不存在或为空将从1开始计数
* 只在 release 模式下执行debug/dev 模式下完全跳过
*/
#[cfg(not(debug_assertions))]
#[cfg(feature = "__preview")]
fn update_version() -> Result<()> {
let version_path = "VERSION";
// VERSION文件的监控已经在main函数中添加此处无需重复
// 读取当前版本号
let mut version = String::new();
let mut file = match File::open(version_path) {
Ok(file) => file,
Err(_) => {
// 如果文件不存在或无法打开从1开始
println!("cargo:warning=VERSION file not found, creating with initial value 1");
let mut new_file = File::create(version_path)?;
new_file.write_all(b"1")?;
return Ok(());
}
};
file.read_to_string(&mut version)?;
// 确保版本号是有效数字
#[allow(unused_variables)]
let version_num = match version.trim().parse::<u64>() {
Ok(num) => num,
Err(_) => {
println!("cargo:warning=Invalid version number in VERSION file. Setting to 1.");
let mut file = File::create(version_path)?;
file.write_all(b"1")?;
return Ok(());
}
};
#[cfg(not(feature = "__preview_locked"))]
{
// 版本号加1
let new_version = version_num + 1;
println!(
"cargo:warning=Release build - bumping version from {version_num} to {new_version}",
);
// 写回文件
let mut file = File::create(version_path)?;
write!(file, "{new_version}")?;
}
Ok(())
}
#[allow(unused)]
fn read_version_number() -> Result<u64> {
let mut version = String::with_capacity(4);
match std::fs::File::open("VERSION") {
Ok(mut file) => {
use std::io::Read as _;
file.read_to_string(&mut version)?;
Ok(version.trim().parse().unwrap_or(1))
}
Err(_) => Ok(1),
}
}
fn generate_build_info() -> Result<()> {
let out_dir = std::env::var("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("build_info.rs");
// #[cfg(debug_assertions)]
// let out_dir = "../target/debug/build/build_info.rs";
// #[cfg(not(debug_assertions))]
// let out_dir = "../target/release/build/build_info.rs";
// let dest_path = Path::new(out_dir);
// if dest_path.is_file() {
// return Ok(());
// }
let build_timestamp =
std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs();
let build_timestamp_str = chrono::DateTime::from_timestamp(build_timestamp as i64, 0)
.unwrap()
.to_rfc3339_opts(chrono::SecondsFormat::Secs, true);
let pkg_version = env!("CARGO_PKG_VERSION");
let (version_str, build_version_str) =
if cfg!(feature = "__preview") && pkg_version.contains("-pre") {
let build_num = read_version_number()?;
(
format!("{pkg_version}+build.{build_num}"),
format!("pub const BUILD_VERSION: u32 = {build_num};\n"),
)
} else {
(pkg_version.to_string(), String::new())
};
let version: Version = version_str.parse().unwrap();
let build_info_content = format!(
r#"// 此文件由 build.rs 自动生成,请勿手动修改
use crate::app::model::version::{{Version, ReleaseStage::Preview}};
{build_version_str}pub const BUILD_TIMESTAMP: &'static str = {build_timestamp_str:?};
/// pub const VERSION_STR: &'static str = {version_str:?};
pub const VERSION: Version = {version:?};
pub const IS_PRERELEASE: bool = {is_prerelease};
pub const IS_DEBUG: bool = {is_debug};
#[cfg(unix)]
pub const BUILD_EPOCH: std::time::SystemTime =
unsafe {{ ::core::intrinsics::transmute(({build_timestamp}i64, 0u32)) }};
#[cfg(windows)]
pub const BUILD_EPOCH: std::time::SystemTime = unsafe {{
const INTERVALS_PER_SEC: u64 = 10_000_000;
const INTERVALS_TO_UNIX_EPOCH: u64 = 11_644_473_600 * INTERVALS_PER_SEC;
const TARGET_INTERVALS: u64 = INTERVALS_TO_UNIX_EPOCH + {build_timestamp} * INTERVALS_PER_SEC;
::core::intrinsics::transmute((
TARGET_INTERVALS as u32,
(TARGET_INTERVALS >> 32) as u32,
))
}};
"#,
is_prerelease = cfg!(feature = "__preview"),
is_debug = cfg!(debug_assertions),
);
std::fs::write(dest_path, build_info_content)?;
Ok(())
}