chore: add customized version

This commit is contained in:
anatawa12 2023-01-23 09:58:17 +09:00
commit 21420e91ab
No known key found for this signature in database
GPG key ID: 9CA909848B8E4EA6
8 changed files with 387 additions and 160 deletions

View file

@ -1,7 +1,7 @@
use crate::version::Version;
use crate::vpm::VersionSelector;
use clap::{Parser, Subcommand};
use reqwest::Url;
use semver::Version;
use std::path::{Path, PathBuf};
#[derive(Subcommand)]

View file

@ -2,6 +2,7 @@ use clap::Parser;
use reqwest::Client;
mod commands;
mod version;
mod vpm;
#[tokio::main]

172
src/version/mod.rs Normal file
View file

@ -0,0 +1,172 @@
pub use range::VersionRange;
use std::fmt::{Display, Formatter};
pub use version::Version;
macro_rules! from_str_impl {
($ty: ty) => {
impl FromStr for $ty {
type Err = ParseRangeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut buffer = ParsingBuf::new(s);
let result = FromParsingBuf::parse(&mut buffer)?;
if buffer.first().is_some() {
return Err(ParseRangeError::invalid_char(buffer.first_char()));
}
Ok(result)
}
}
};
}
macro_rules! serialize_to_string {
($ty: ty) => {
impl ::serde::Serialize for $ty {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ::serde::Serializer,
{
serializer.serialize_str(&std::string::ToString::to_string(self))
}
}
};
}
macro_rules! deserialize_from_str {
($ty: ty, $name: literal) => {
impl<'de> ::serde::de::Deserialize<'de> for $ty {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: ::serde::de::Deserializer<'de>,
{
struct Visitor;
impl<'de> ::serde::de::Visitor<'de> for Visitor {
type Value = $ty;
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
formatter.write_str($name)
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: ::serde::de::Error,
{
std::str::FromStr::from_str(v).map_err(E::custom)
}
}
deserializer.deserialize_str(Visitor)
}
}
};
}
mod range;
mod version;
type Segment = u64;
const NOT_EXISTS: Segment = Segment::MAX;
const STAR: Segment = NOT_EXISTS - 1;
const UPPER_X: Segment = STAR - 1;
const LOWER_X: Segment = UPPER_X - 1;
const VERSION_SEGMENT_MAX: Segment = LOWER_X - 1;
trait FromParsingBuf: Sized {
fn parse(buffer: &mut ParsingBuf) -> Result<Self, ParseRangeError>;
}
struct ParsingBuf<'a> {
buf: &'a str,
}
impl<'a> ParsingBuf<'a> {
pub fn new(source: &'a str) -> ParsingBuf {
Self { buf: source }
}
fn is_empty(&self) -> bool {
self.buf.is_empty()
}
pub fn read(&mut self, ch: char) -> Result<(), ParseRangeError> {
match self.buf.chars().next() {
Some(c) if c == ch => {
self.skip();
Ok(())
}
Some(c) => Err(ParseRangeError::invalid_char(c)),
None => Err(ParseRangeError::unexpected_end()),
}
}
pub fn first(&self) -> Option<u8> {
self.buf.as_bytes().first().copied()
}
pub fn first_char(&self) -> char {
self.buf.chars().next().expect("invalid state")
}
pub fn skip(&mut self) -> &mut Self {
if self.buf.len() != 0 {
self.buf = &self.buf[1..];
}
self
}
pub fn get(&self, index: usize) -> Option<u8> {
self.buf.as_bytes().get(index).copied()
}
pub fn slip_ws(&mut self) {
self.buf = self.buf.trim_start();
}
pub fn take(&mut self, count: usize) -> &str {
let (a, b) = self.buf.split_at(count);
self.buf = b;
a
}
}
#[derive(Debug)]
pub struct ParseRangeError {
inner: Inner,
}
impl Display for ParseRangeError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self.inner {
Inner::VersionSegmentTooBig => f.write_str("version segment too big"),
Inner::UnexpectedEnd => f.write_str("unexpected end"),
Inner::InvalidChar(c) => write!(f, "invalid char: {:?}", c),
}
}
}
impl std::error::Error for ParseRangeError {}
#[derive(Debug)]
enum Inner {
VersionSegmentTooBig,
UnexpectedEnd,
InvalidChar(char),
}
impl ParseRangeError {
fn too_big() -> Self {
Self {
inner: Inner::VersionSegmentTooBig,
}
}
fn invalid_char(c: char) -> Self {
Self {
inner: Inner::InvalidChar(c),
}
}
fn unexpected_end() -> ParseRangeError {
Self {
inner: Inner::UnexpectedEnd,
}
}
}

View file

@ -1,8 +1,5 @@
use crate::vpm::version::{FromParsingBuf, ParseRangeError, ParsingBuf};
use semver::{BuildMetadata, Prerelease, Version};
use serde::de;
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::version::*;
use semver::{BuildMetadata, Prerelease};
use std::fmt::{Display, Formatter, Write};
use std::str::FromStr;
@ -20,38 +17,8 @@ impl VersionRange {
}
}
impl Serialize for VersionRange {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for VersionRange {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = VersionRange;
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
formatter.write_str("version range")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: Error,
{
VersionRange::from_str(v).map_err(E::custom)
}
}
deserializer.deserialize_str(Visitor)
}
}
serialize_to_string!(VersionRange);
deserialize_from_str!(VersionRange, "version range");
impl Display for VersionRange {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
@ -99,18 +66,7 @@ impl Display for ComparatorSet {
}
}
impl FromStr for ComparatorSet {
type Err = ParseRangeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut buffer = ParsingBuf::new(s);
let result = FromParsingBuf::parse(&mut buffer)?;
if buffer.first().is_some() {
return Err(ParseRangeError::invalid_char(buffer.first_char()));
}
Ok(result)
}
}
from_str_impl!(ComparatorSet);
impl FromParsingBuf for ComparatorSet {
fn parse(buffer: &mut ParsingBuf) -> Result<Self, ParseRangeError> {
@ -248,7 +204,7 @@ impl Comparator {
Some(v) => version >= &v,
None => {
let zeros = v.to_zeros();
version >= &zeros || !version.pre.is_empty() && base_version(version) == zeros
version >= &zeros || !version.pre.is_empty() && version.base_version() == zeros
}
}
}
@ -257,7 +213,8 @@ impl Comparator {
Some(v) => version >= &v,
None => {
let zeros = v.to_zeros();
version < &zeros || !(!version.pre.is_empty() && base_version(version) == zeros)
version < &zeros
|| !(!version.pre.is_empty() && version.base_version() == zeros)
}
};
}
@ -320,14 +277,6 @@ impl FromParsingBuf for Comparator {
}
}
type Segment = u64;
const NOT_EXISTS: Segment = Segment::MAX;
const STAR: Segment = NOT_EXISTS - 1;
const UPPER_X: Segment = STAR - 1;
const LOWER_X: Segment = UPPER_X - 1;
const VERSION_SEGMENT_MAX: Segment = LOWER_X - 1;
#[derive(Debug, Clone)]
struct PartialVersion {
// MAX_VALUE for not exists
@ -373,10 +322,6 @@ impl Display for PartialVersion {
}
}
fn base_version(version: &Version) -> Version {
Version::new(version.major, version.minor, version.patch)
}
impl PartialVersion {
fn to_full(&self) -> Option<Version> {
if let (Some(major), Some(minor), Some(patch)) = (self.major(), self.minor(), self.patch())

203
src/version/version.rs Normal file
View file

@ -0,0 +1,203 @@
use super::*;
use semver::{BuildMetadata, Prerelease};
use std::cmp::Ordering;
use std::fmt::{Display, Formatter, Write};
use std::str::FromStr;
/// custom version implementation to avoid compare build meta
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Version {
pub major: Segment,
pub minor: Segment,
pub patch: Segment,
pub pre: Prerelease,
pub build: BuildMetadata,
}
from_str_impl!(Version);
serialize_to_string!(Version);
deserialize_from_str!(Version, "version");
impl Display for Version {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}.{}.{}", self.major, self.minor, self.patch)?;
if !self.pre.is_empty() {
f.write_char('-')?;
Display::fmt(&self.pre, f)?;
}
if !self.build.is_empty() {
f.write_char('+')?;
Display::fmt(&self.build, f)?;
}
Ok(())
}
}
impl FromParsingBuf for Version {
fn parse(bytes: &mut ParsingBuf) -> Result<Self, ParseRangeError> {
bytes.slip_ws();
let major = parse_segment(bytes)?;
bytes.read('.')?;
let minor = parse_segment(bytes)?;
bytes.read('.')?;
let patch = parse_segment(bytes)?;
let pre = if let Some(b'-') = bytes.first() {
bytes.skip();
Prerelease::parse(bytes)?
} else {
Prerelease::EMPTY
};
let build = if let Some(b'+') = bytes.first() {
bytes.skip();
BuildMetadata::parse(bytes)?
} else {
BuildMetadata::EMPTY
};
return Ok(Version {
major,
minor,
patch,
pre,
build,
});
fn parse_segment(bytes: &mut ParsingBuf) -> Result<Segment, ParseRangeError> {
match bytes.first() {
Some(b'1'..=b'9') => {
let mut i = 1;
while let Some(b'0'..=b'9') = bytes.get(i) {
i += 1;
}
let str = bytes.take(i);
let value = Segment::from_str(str).map_err(|_| ParseRangeError::too_big())?;
if value > VERSION_SEGMENT_MAX {
return Err(ParseRangeError::too_big());
}
Ok(value)
}
Some(b'0') => {
bytes.skip();
// if 0\d, 0 is invalid char
if let Some(b'0'..=b'9') = bytes.first() {
return Err(ParseRangeError::invalid_char(bytes.first_char()));
}
Ok(0)
}
Some(_) => Err(ParseRangeError::invalid_char(bytes.first_char())),
None => Err(ParseRangeError::unexpected_end()),
}
}
}
}
impl PartialOrd<Self> for Version {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(Ord::cmp(self, other))
}
}
impl Ord for Version {
fn cmp(&self, other: &Self) -> Ordering {
self.major
.cmp(&other.major)
.then_with(|| self.minor.cmp(&other.minor))
.then_with(|| self.patch.cmp(&other.patch))
.then_with(|| self.pre.cmp(&other.pre))
}
}
impl Version {
pub fn new(major: Segment, minor: Segment, patch: Segment) -> Version {
Version {
major,
minor,
patch,
pre: Prerelease::EMPTY,
build: BuildMetadata::EMPTY,
}
}
pub fn base_version(&self) -> Version {
Version::new(self.major, self.minor, self.patch)
}
}
impl FromParsingBuf for Prerelease {
fn parse(buffer: &mut ParsingBuf) -> Result<Self, ParseRangeError> {
Ok(Prerelease::new(parse_id(buffer, false)?).unwrap())
}
}
impl FromParsingBuf for BuildMetadata {
fn parse(buffer: &mut ParsingBuf) -> Result<Self, ParseRangeError> {
Ok(BuildMetadata::new(parse_id(buffer, true)?).unwrap())
}
}
fn parse_id<'a>(
bytes: &mut ParsingBuf<'a>,
allow_loading_zero: bool,
) -> Result<&'a str, ParseRangeError> {
let buf = bytes.buf;
'outer: loop {
let mut leading_zero = false;
let mut alphanumeric = false;
match bytes.first() {
None => return Err(ParseRangeError::unexpected_end()),
Some(b'0') => {
bytes.skip();
leading_zero = true;
}
Some(b'0'..=b'9') => {
bytes.skip();
}
Some(b'a'..=b'z' | b'A'..=b'Z' | b'-') => {
bytes.skip();
alphanumeric = true;
}
Some(b'.') => return Err(ParseRangeError::invalid_char('.')),
_ => return Err(ParseRangeError::invalid_char(bytes.first_char())),
}
'segment: loop {
match bytes.first() {
Some(b'0'..=b'9') => {
bytes.skip();
}
Some(b'a'..=b'z' | b'A'..=b'Z' | b'-') => {
bytes.skip();
alphanumeric = true;
}
Some(b'.') => {
bytes.skip();
if !allow_loading_zero && alphanumeric && leading_zero {
// leading zero is invalid char
return Err(ParseRangeError::invalid_char('0'));
}
break 'segment;
}
_ => {
// end of segment
if !allow_loading_zero && alphanumeric && leading_zero {
// leading zero is invalid char
return Err(ParseRangeError::invalid_char('0'));
}
break 'outer;
}
}
}
}
if bytes.buf.len() == 0 {
Ok(buf)
} else {
let len = bytes.buf.as_ptr() as usize - buf.as_ptr() as usize;
Ok(&buf[..len])
}
}

View file

@ -2,16 +2,15 @@
//!
//! This module might be a separated crate.
use crate::version::{Version, VersionRange};
use crate::vpm::structs::manifest::{VpmDependency, VpmLockedDependency, VpmManifest};
use crate::vpm::structs::package::PackageJson;
use crate::vpm::structs::repository::{LocalCachedRepository, RemoteRepository};
use crate::vpm::structs::setting::UserRepoSetting;
use crate::vpm::structs::VersionRange;
use crate::vpm::utils::{MapResultExt, PathBufExt};
use futures::future::join_all;
use indexmap::IndexMap;
use reqwest::{Client, IntoUrl, Url};
use semver::Version;
use std::collections::hash_map::Entry;
use std::collections::{HashMap, HashSet};
use std::ffi::{OsStr, OsString};
@ -27,7 +26,6 @@ use tokio::io::{AsyncRead, AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
pub mod structs;
mod utils;
mod version;
static VRC_OFFICIAL_URL: &'static str = "https://packages.vrchat.com/official?download";
static VRC_CURATED_URL: &'static str = "https://packages.vrchat.com/curated?download";

View file

@ -1,6 +1,5 @@
pub use crate::vpm::version::VersionRange;
use crate::version::{Version, VersionRange};
use indexmap::IndexMap;
use semver::Version;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::path::PathBuf;

View file

@ -1,91 +0,0 @@
pub use range::VersionRange;
use std::fmt::{Display, Formatter};
mod range;
trait FromParsingBuf: Sized {
fn parse(buffer: &mut ParsingBuf) -> Result<Self, ParseRangeError>;
}
struct ParsingBuf<'a> {
buf: &'a str,
}
impl<'a> ParsingBuf<'a> {
pub fn new(source: &'a str) -> ParsingBuf {
Self { buf: source }
}
fn is_empty(&self) -> bool {
self.buf.is_empty()
}
pub fn first(&self) -> Option<u8> {
self.buf.as_bytes().first().copied()
}
pub fn first_char(&self) -> char {
self.buf.chars().next().expect("invalid state")
}
pub fn skip(&mut self) -> &mut Self {
if self.buf.len() != 0 {
self.buf = &self.buf[1..];
}
self
}
pub fn get(&self, index: usize) -> Option<u8> {
self.buf.as_bytes().get(index).copied()
}
pub fn slip_ws(&mut self) {
self.buf = self.buf.trim_start();
}
pub fn take(&mut self, count: usize) -> &str {
let (a, b) = self.buf.split_at(count);
self.buf = b;
a
}
}
#[derive(Debug)]
pub struct ParseRangeError {
inner: Inner,
}
impl Display for ParseRangeError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self.inner {
Inner::VersionSegmentTooBig => f.write_str("version segment too big"),
Inner::UnexpectedEnd => f.write_str("unexpected end"),
Inner::InvalidChar(c) => write!(f, "invalid char: {:?}", c),
}
}
}
#[derive(Debug)]
enum Inner {
VersionSegmentTooBig,
UnexpectedEnd,
InvalidChar(char),
}
impl ParseRangeError {
fn too_big() -> Self {
Self {
inner: Inner::VersionSegmentTooBig,
}
}
fn invalid_char(c: char) -> Self {
Self {
inner: Inner::InvalidChar(c),
}
}
fn unexpected_end() -> ParseRangeError {
Self {
inner: Inner::UnexpectedEnd,
}
}
}