#semver #version #version-string #parser #semantic #lenient

lenient_semver_version_builder

用于语义版本号的宽松解析器 VersionBuilder 特性

3 个版本

0.4.2 2021 年 6 月 6 日
0.4.1 2021 年 6 月 6 日
0.4.0 2021 年 6 月 6 日

#2929解析器实现

Download history 13461/week @ 2024-03-14 12183/week @ 2024-03-21 10820/week @ 2024-03-28 13071/week @ 2024-04-04 12148/week @ 2024-04-11 13620/week @ 2024-04-18 11775/week @ 2024-04-25 13099/week @ 2024-05-02 13503/week @ 2024-05-09 13896/week @ 2024-05-16 13931/week @ 2024-05-23 14815/week @ 2024-05-30 12628/week @ 2024-06-06 12612/week @ 2024-06-13 12678/week @ 2024-06-20 9736/week @ 2024-06-27

50,138 每月下载量
用于 60 个crate(直接使用 3 个)

MIT/Apache

18KB
342

宽松语义版本号解析器

宽松解析语义版本号。

动机

此crate旨在为semver Version提供替代解析器。

与遵守semver规范相比,此解析器在允许的内容上更为宽松。区别包括

  • 次要和路径是可选的,默认为0(例如,“1”解析为“1.0.0”)
  • 预发布标识符也可以通过.分隔(例如,“1.2.3.rc1”解析为“1.2.3-rc1”)
  • 一些预发布标识符被解析为构建标识符(例如,“1.2.3.Final”解析为“1.2.3+Final”)
  • 额外的数字标识符被解析为构建标识符(例如 "1.2.3.4.5" 解析为 "1.2.3+4.5")
  • 允许以vV开头(例如,“v1.2.3”解析为“1.2.3”)
  • 超出u64范围的数字被视为字符串(例如,“1.2.3-9876543210987654321098765432109876543210”可以无错误地解析)

此图显示了宽松解析语法

have a look at doc/railroad.svg

示例

use semver::Version;

let version = lenient_semver::parse("1.2.3");
assert_eq!(version, Ok(Version::new(1, 2, 3)));

// examples of a version that would not be accepted by semver_parser
assert_eq!(
    lenient_semver::parse("1.2.M1").unwrap(),
    Version::parse("1.2.0-M1").unwrap()
);
assert!(Version::parse("1.2.M1").is_err());

assert_eq!(
    lenient_semver::parse("1").unwrap(),
    Version::parse("1.0.0").unwrap()
);
assert!(Version::parse("1").is_err());

assert_eq!(
    lenient_semver::parse("1.2.3.Final").unwrap(),
    Version::parse("1.2.3+Final").unwrap()
);
assert!(Version::parse("1.2.3.Final").is_err());

assert_eq!(
    lenient_semver::parse("1.2.3.4.5").unwrap(),
    Version::parse("1.2.3+4.5").unwrap()
);
assert!(Version::parse("1.2.3.4.5").is_err());

assert_eq!(
    lenient_semver::parse("v1.2.3").unwrap(),
    Version::parse("1.2.3").unwrap()
);
assert!(Version::parse("v1.2.3").is_err());

assert_eq!(
    lenient_semver::parse("1.2.9876543210987654321098765432109876543210").unwrap(),
    Version::parse("1.2.0-9876543210987654321098765432109876543210").unwrap()
);
assert!(Version::parse("1.2.9876543210987654321098765432109876543210").is_err());

解析到自定义版本

解析器不是固定返回semver::Version,而是解析到lenient_semver::VersionBuilder。此crate的默认功能包含一个用于semver::VersionVersionBuilder实现,但任何实现都可以与parse_into一起使用。

示例

use lenient_semver::VersionBuilder;

/// Simpler version struct that lives only on the stack
#[derive(Debug, Default)]
struct MyVersion {
    numbers: [u64; 3],
    is_pre_release: bool,
}

/// The VersionBuilder trait is generic over the lifetime of the input string.
/// We don't store references to those strings, so we don't care about the specific lifetime.
impl VersionBuilder<'_> for MyVersion {
    /// We will modify the target struct directly
    type Out = Self;

    /// Construct a new builder instance.
    /// One can only expect `set_major` to be called before `build`, all other methods are optional.
    fn new() -> Self {
        Self::default()
    }

    /// Construct the final result. In this case, we can just return ourselves.
    fn build(self) -> Self::Out {
        self
    }

    /// Called when the major component was found.
    fn set_major(&mut self, major: u64) {
        self.numbers[0] = major;
    }

    /// Called when the minor component was found.
    fn set_minor(&mut self, minor: u64) {
        self.numbers[1] = minor;
    }

    /// Called when the patch component was found.
    fn set_patch(&mut self, patch: u64) {
        self.numbers[2] = patch;
    }

    /// Called when any pre-relase metadata identifier was found.
    /// This identifier can just numeric, no attempts at parsing it into a number have been made.
    /// For this implementation, we don't care about the value, just it's presence.
    fn add_pre_release(&mut self, _pre_release: &str) {
        self.is_pre_release = true
    }
}

let input = "1.3.3.7-alpha21+build.42";
let my_version = lenient_semver::parse_into::<MyVersion>(input).unwrap();

assert_eq!([1, 3, 3], my_version.numbers);
assert!(my_version.is_pre_release);

VersionBuilder为各种方法提供了空的默认实现,这使得它易于用于解析以外的用例。以下示例实现了检查给定字符串是否表示任何形式的预发布版本的函数。

use lenient_semver::VersionBuilder;

/// newtype around bool, so we can implement the VersionBuilder trait for it
#[derive(Debug, Default)]
struct IsPreRelease(bool);

impl VersionBuilder<'_> for IsPreRelease {
    /// Here we parse into a different value than Self
    type Out = bool;

    fn new() -> Self {
        Self::default()
    }

    /// Return the wrapped bool
    fn build(self) -> Self::Out {
        self.0
    }

    /// We only care about this method and can ignore all the other ones
    fn add_pre_release(&mut self, _pre_release: &str) {
        self.0 = true;
    }
}

/// This method also return false for invalid version strings,
/// which is technically true, as those are not pre-release versions.
/// Usually you would want to have a better error handling.
fn is_pre_release(v: &str) -> bool {
    lenient_semver::parse_into::<IsPreRelease>(v).unwrap_or_default()
}

assert!(is_pre_release("1.2.3-pre") == true);
assert!(is_pre_release("1.2.3") == false);
assert!(is_pre_release("1.2.3+build") == false);

功能

lenient_semver包含一些功能

功能名称 默认启用 传递依赖 用途
semver11 semver= "0.11.0" semver = "0.11.0"提供VersionBuilder实现。
semver10 semver= "0.10.0" VersionBuilder提供了实现,用于semver = "0.10.0"
version_lite lenient_version= "*" 一种自定义版本实现,作为semver::Version的替代,以补充一些宽松特性,例如超出补丁级别的额外数字。
version_semver lenient_version= "*" lenient_version转换为semver::Version
version_serde serde= "1" lenient_version实现Serde反序列化和序列化。

示例

semver11
lenient_semver = { version = "*", features = [ "semver11" ] }
use semver::Version as Version11;

// This features is enabled by default and is usable through `parse` directly,
// but can also be used with `parse_into`.
let version = lenient_semver::parse_into::<Version11>("v1.2.3.Final").unwrap();
assert_eq!(version, Version11::parse("1.2.3+Final").unwrap());
semver10
lenient_semver = { version = "*", features = [ "semver10" ] }
// We have both version of semver available, the older one
// is renamed to `semver010`.
use semver010::Version as Version10;

// The default parse is fixed to the latest semver::Version,
// so we need to use `parse_into`.
let version = lenient_semver::parse_into::<Version10>("v1.2.3.Final").unwrap();
assert_eq!(version, Version10::parse("1.2.3+Final").unwrap());
version_lite
lenient_semver = { version = "*", features = [ "version_lite" ] }

有了这些特性,lenient_semver现在有了自己的版本。该特定实现直接支持补丁级别以上的数字。请注意,lenient_semver仍然会解析这些额外数字而不报错,但它们被添加为semver版本的构建属性。

use lenient_semver::Version;

let version = lenient_semver::parse_into::<Version>("1.3.3.7").unwrap();
assert_eq!(version, Version::parse("1.3.3.7").unwrap()); // Version::parse delegates to this parser

本地支持允许这样的版本正确比较,这在使用semver时是不行的。

use lenient_semver::Version;

let version_a = Version::parse("1.3.3.7").unwrap();
let version_b = Version::parse("1.3.3.8").unwrap();
assert!(version_a < version_b);

// with semver, that fails:
let version_a = lenient_semver::parse("1.3.3.7").unwrap();
let version_b = lenient_semver::parse("1.3.3.8").unwrap();
assert_eq!(version_a < version_b, false);
assert_eq!(version_a, version_b);

此外,Version不拥有元数据标识符的数据。元数据可以分离,因此版本可以引用不同的所有者。

use lenient_semver::{Version, VersionBuilder};

let input = "1.3.3.7-beta.21+build.42";
// make an owned copy, so we don't cheat by using the 'static lifetime.
let input = String::from(input);

// This version references slices from the `input` String
let version = lenient_semver::parse_into::<Version>(input.as_ref()).unwrap();

// Which prevents us from dropping the input
// drop(input);

// We can disassociate the metadata, which allows the new version to reference something else
let (mut version, pre, build) = version.disassociate_metadata();

// We still get the referenced input slices, so we create owned copies
let pre: Vec<String> = pre.into_iter().map(ToOwned::to_owned).collect();
let build: Vec<String> = build.into_iter().map(ToOwned::to_owned).collect();

// now we can safely drop the input
drop(input);

// We can also re-add the cloned identifiers.
// The version would now be bound to the lifetime of this method.
// Just for fun, we swap pre-release and build
for pre in &pre {
    version.add_build(pre.as_ref());
}
for build in &build {
    version.add_pre_release(build.as_ref());
}

assert_eq!("1.3.3.7-build.42+beta.21".to_string(), version.to_string());
version_semver
lenient_semver = { version = "*", features = [ "version_semver" ] }

如果您需要存储版本信息的所有者副本,您应该将其复制到semver::Version或您的自定义版本类型中。如果您仅打算存储版本信息,直接解析到semver::Version可能更合理。

use semver::Version;

let input = String::from("v1.3.3.7-beta-21+build-42");
let version = lenient_semver::Version::parse(&input).unwrap();
let version = Version::from(version);
assert_eq!("1.3.3-beta.21+7.build.42", &version.to_string());
version_serde
lenient_semver = { version = "*", features = [ "version_serde" ] }

此功能还启用了version_lite,并为自己的版本类型提供了serde支持。由于lenient_semver::Version不拥有元数据标识符,反序列化结果的生存期绑定到输入。

use lenient_semver::{Version, VersionBuilder};
use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct DependencySpec<'input> {
    /// Refer to name as owned value
    name: String,
    /// Borrows from the input string
    #[serde(borrow)]
    version: Version<'input>,
}

let input = "
    {
        \"name\": \"lenient_semver\",
        \"version\": \"1.3.3.7+build.42\"
    }";
// make an owned copy, so we don't cheat by using the 'static lifetime.
let input = String::from(input);

// use serde as one would normally do
let dep: DependencySpec = serde_json::from_str(input.as_ref()).unwrap();
println!("{:?}", dep);

// cannot move out of `input` because it is borrowed
// drop(input);

let mut expected = Version::new(1, 3, 3);
expected.add_additional(7);
expected.add_build("build");
expected.add_build("42");

assert_eq!(dep.version, expected);

// now we can drop the input
drop(input);
parse_partial
lenient_semver = { version = "*", features = [ "parse_partial" ] }

此功能启用了解析器的partial特性。部分解析器不会尝试消费所有输入。相反,它会尽可能解析版本,并将未消费的输入与解析的版本一起返回。

use lenient_semver::{Version, VersionBuilder, parser};
let input = "1.2.3   42+build 1.3.3.7 // end";

// parse first version
let (version, remainder) = parser::parse_partial::<Version>(input).unwrap();
let expected = Version::new(1, 2, 3);
assert_eq!(version, expected);
// trailing whitespace is considered part of a version and consumed as well
assert_eq!("42+build 1.3.3.7 // end", remainder);

// parse second version
let (version, remainder) = parser::parse_partial::<Version>(remainder).unwrap();
let mut expected = Version::new(42, 0, 0);
expected.add_build("build");
assert_eq!(version, expected);
assert_eq!("1.3.3.7 // end", remainder);

// parse last version
let (version, remainder) = parser::parse_partial::<Version>(remainder).unwrap();
let mut expected = Version::new(1, 3, 3);
expected.add_additional(7);
assert_eq!(version, expected);
assert_eq!("// end", remainder);

// parse partial still expects to parse something.
// It will fail with `UnexpectedInput` or `MissingMajorNumber` if the input does not match at least a major version.
// let's try to parse the remaining input
let error = parser::parse_partial::<Version>(remainder).unwrap_err();
assert_eq!(error.error_kind(), parser::ErrorKind::UnexpectedInput);
assert_eq!(error.error_line(), "Unexpected `/`");

// or an empty string
let error = parser::parse_partial::<Version>("         ").unwrap_err();
assert_eq!(error.error_kind(), parser::ErrorKind::MissingMajorNumber);
assert_eq!(
    error.error_line(),
    "Could not parse the major identifier: No input"
);

// The rules of when a certain number will be parsed are even more relaxed
let (version, remainder) = parser::parse_partial::<Version>("1foobar").unwrap();
let expected = Version::new(1, 0, 0);
assert_eq!(version, expected);
assert_eq!(remainder, "foobar");

// Furthermore, the characters `*` and `?` are allowed to appear everywhere where other alphabetic character are allowed.
// This relaxes the rule that only a-z, A-Z, and 0-9 are allowed.
// Those characters have no special meaning and will be parsed as pre-release or build segment.
let (version, remainder) = parser::parse_partial::<Version>("1.2.*+final?").unwrap();
let mut expected = Version::new(1, 2, 0);
expected.add_pre_release("*");
expected.add_build("final?");
assert_eq!(version, expected);
assert_eq!(remainder, "");

许可证:MIT OR Apache-2.0


lib.rs:

宽松解析语义版本号。

依赖关系

~66–350KB