{"id":15691085,"url":"https://github.com/knutwalker/lenient-semver","last_synced_at":"2026-03-07T08:33:19.189Z","repository":{"id":43694535,"uuid":"295055990","full_name":"knutwalker/lenient-semver","owner":"knutwalker","description":"Lenient parser for Semantic Version numbers in Rust","archived":false,"fork":false,"pushed_at":"2023-02-13T12:57:36.000Z","size":1566,"stargazers_count":12,"open_issues_count":4,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2026-02-24T15:44:32.246Z","etag":null,"topics":["hacktoberfest","rust","rustoberfest","semantic-version","semver","semver-parser"],"latest_commit_sha":null,"homepage":"https://docs.rs/lenient_semver/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/knutwalker.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-09-13T01:26:03.000Z","updated_at":"2025-11-26T14:55:43.000Z","dependencies_parsed_at":"2024-10-09T13:45:23.684Z","dependency_job_id":null,"html_url":"https://github.com/knutwalker/lenient-semver","commit_stats":{"total_commits":115,"total_committers":4,"mean_commits":28.75,"dds":"0.29565217391304344","last_synced_commit":"3585c592a885af4ac836a8f23b949daa3adb1257"},"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"purl":"pkg:github/knutwalker/lenient-semver","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knutwalker%2Flenient-semver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knutwalker%2Flenient-semver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knutwalker%2Flenient-semver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knutwalker%2Flenient-semver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/knutwalker","download_url":"https://codeload.github.com/knutwalker/lenient-semver/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knutwalker%2Flenient-semver/sbom","scorecard":{"id":564974,"data":{"date":"2025-08-11","repo":{"name":"github.com/knutwalker/lenient-semver","commit":"3585c592a885af4ac836a8f23b949daa3adb1257"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.4,"checks":[{"name":"Code-Review","score":0,"reason":"Found 1/29 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/checks.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/checks.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/knutwalker/lenient-semver/checks.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/checks.yml:25: update your workflow using https://app.stepsecurity.io/secureworkflow/knutwalker/lenient-semver/checks.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/checks.yml:32: update your workflow using https://app.stepsecurity.io/secureworkflow/knutwalker/lenient-semver/checks.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/checks.yml:34: update your workflow using https://app.stepsecurity.io/secureworkflow/knutwalker/lenient-semver/checks.yml/master?enable=pin","Info:   0 out of   1 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   3 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 2 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-20T14:43:54.235Z","repository_id":43694535,"created_at":"2025-08-20T14:43:54.235Z","updated_at":"2025-08-20T14:43:54.235Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30209942,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-07T05:23:27.321Z","status":"ssl_error","status_checked_at":"2026-03-07T05:00:17.256Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hacktoberfest","rust","rustoberfest","semantic-version","semver","semver-parser"],"created_at":"2024-10-03T18:20:13.475Z","updated_at":"2026-03-07T08:33:19.148Z","avatar_url":"https://github.com/knutwalker.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Lenient Semantic Version Parser\n\nLenient parser for Semantic Version numbers.\n\n### Motivation\n\nThis crate aims to provide an alternative parser for [semver `Version`s](https://crates.io/crates/semver).\n\nInstead of adhering to the semver specification, this parser is more lenient in what it allows.\nThe differences include:\n\n- Minor and Path are optional an default to 0 (e.g. \"1\" parses as \"1.0.0\")\n- Pre-release identifier may be separated by `.` as well (e.g. \"1.2.3.rc1\" parses as \"1.2.3-rc1\")\n- Some pre-release identifiers are parsed as build identifier (e.g. \"1.2.3.Final\" parses as \"1.2.3+Final\")\n- Additional numeric identifiers are parsed as build identifier (e.g \"1.2.3.4.5\" parses as \"1.2.3+4.5\")\n- A leading `v` or `V` is allowed (e.g. \"v1.2.3\" parses as \"1.2.3\")\n- Numbers that overflow an u64 are treated as strings (e.g. \"1.2.3-9876543210987654321098765432109876543210\" parses without error)\n\nThis diagram shows lenient parsing grammar\n\n![have a look at doc/railroad.svg](https://knutwalker.s3.eu-central-1.amazonaws.com/lenient-semver/doc/railroad.svg)\n\n### Examples\n\n```rust\nuse semver::Version;\n\nlet version = lenient_semver::parse(\"1.2.3\");\nassert_eq!(version, Ok(Version::new(1, 2, 3)));\n\n// examples of a version that would not be accepted by semver_parser\nassert_eq!(\n    lenient_semver::parse(\"1.2.M1\").unwrap(),\n    Version::parse(\"1.2.0-M1\").unwrap()\n);\nassert!(Version::parse(\"1.2.M1\").is_err());\n\nassert_eq!(\n    lenient_semver::parse(\"1\").unwrap(),\n    Version::parse(\"1.0.0\").unwrap()\n);\nassert!(Version::parse(\"1\").is_err());\n\nassert_eq!(\n    lenient_semver::parse(\"1.2.3.Final\").unwrap(),\n    Version::parse(\"1.2.3+Final\").unwrap()\n);\nassert!(Version::parse(\"1.2.3.Final\").is_err());\n\nassert_eq!(\n    lenient_semver::parse(\"1.2.3.4.5\").unwrap(),\n    Version::parse(\"1.2.3+4.5\").unwrap()\n);\nassert!(Version::parse(\"1.2.3.4.5\").is_err());\n\nassert_eq!(\n    lenient_semver::parse(\"v1.2.3\").unwrap(),\n    Version::parse(\"1.2.3\").unwrap()\n);\nassert!(Version::parse(\"v1.2.3\").is_err());\n\nassert_eq!(\n    lenient_semver::parse(\"1.2.9876543210987654321098765432109876543210\").unwrap(),\n    Version::parse(\"1.2.0-9876543210987654321098765432109876543210\").unwrap()\n);\nassert!(Version::parse(\"1.2.9876543210987654321098765432109876543210\").is_err());\n```\n\n### Parsing into custom versions\n\nThe parser is not fixed on returning a `semver::Version`, it instead parses into a `lenient_semver::VersionBuilder`.\nThe default features for this crate contain a `VersionBuilder` implementation for `semver::Version`, but any implementation can be used with `parse_into`.\n\n#### Examples\n\n```rust\nuse lenient_semver::VersionBuilder;\n\n/// Simpler version struct that lives only on the stack\n#[derive(Debug, Default)]\nstruct MyVersion {\n    numbers: [u64; 3],\n    is_pre_release: bool,\n}\n\n/// The VersionBuilder trait is generic over the lifetime of the input string.\n/// We don't store references to those strings, so we don't care about the specific lifetime.\nimpl VersionBuilder\u003c'_\u003e for MyVersion {\n    /// We will modify the target struct directly\n    type Out = Self;\n\n    /// Construct a new builder instance.\n    /// One can only expect `set_major` to be called before `build`, all other methods are optional.\n    fn new() -\u003e Self {\n        Self::default()\n    }\n\n    /// Construct the final result. In this case, we can just return ourselves.\n    fn build(self) -\u003e Self::Out {\n        self\n    }\n\n    /// Called when the major component was found.\n    fn set_major(\u0026mut self, major: u64) {\n        self.numbers[0] = major;\n    }\n\n    /// Called when the minor component was found.\n    fn set_minor(\u0026mut self, minor: u64) {\n        self.numbers[1] = minor;\n    }\n\n    /// Called when the patch component was found.\n    fn set_patch(\u0026mut self, patch: u64) {\n        self.numbers[2] = patch;\n    }\n\n    /// Called when any pre-relase metadata identifier was found.\n    /// This identifier can just numeric, no attempts at parsing it into a number have been made.\n    /// For this implementation, we don't care about the value, just it's presence.\n    fn add_pre_release(\u0026mut self, _pre_release: \u0026str) {\n        self.is_pre_release = true\n    }\n}\n\nlet input = \"1.3.3.7-alpha21+build.42\";\nlet my_version = lenient_semver::parse_into::\u003cMyVersion\u003e(input).unwrap();\n\nassert_eq!([1, 3, 3], my_version.numbers);\nassert!(my_version.is_pre_release);\n```\n\nThe VersionBuilder has empty default implementation for the various methods, making it easy to use it for use-cases beyond just parsing.\nThe following example implements a function that checks if a given string represents any form of pre-release version.\n\n```rust\nuse lenient_semver::VersionBuilder;\n\n/// newtype around bool, so we can implement the VersionBuilder trait for it\n#[derive(Debug, Default)]\nstruct IsPreRelease(bool);\n\nimpl VersionBuilder\u003c'_\u003e for IsPreRelease {\n    /// Here we parse into a different value than Self\n    type Out = bool;\n\n    fn new() -\u003e Self {\n        Self::default()\n    }\n\n    /// Return the wrapped bool\n    fn build(self) -\u003e Self::Out {\n        self.0\n    }\n\n    /// We only care about this method and can ignore all the other ones\n    fn add_pre_release(\u0026mut self, _pre_release: \u0026str) {\n        self.0 = true;\n    }\n}\n\n/// This method also return false for invalid version strings,\n/// which is technically true, as those are not pre-release versions.\n/// Usually you would want to have a better error handling.\nfn is_pre_release(v: \u0026str) -\u003e bool {\n    lenient_semver::parse_into::\u003cIsPreRelease\u003e(v).unwrap_or_default()\n}\n\nassert!(is_pre_release(\"1.2.3-pre\") == true);\nassert!(is_pre_release(\"1.2.3\") == false);\nassert!(is_pre_release(\"1.2.3+build\") == false);\n```\n\n### Features\n\n`lenient_semver` comes with a number of features:\n\n\n|   feature name | default enabled | transitive dependencies | purpose\n| -------------: | --------------- | ----------------------- | --------\n|       semver11 | **yes**         | `semver = \"0.11.0\"`     | Provides `VersionBuilder` implementation for `semver = \"0.11.0\"`.\n|       semver10 | no              | `semver = \"0.10.0\"`     | Provides `VersionBuilder` implementation for `semver = \"0.10.0\"`.\n|   version_lite | no              | `lenient_version = \"*\"` | A custom Version as alternative to `semver::Version` that complements some leneient features, such as additional numbers beyond patch.\n| version_semver | no              | `lenient_version = \"*\"` | Add conversions From `lenient_version` Into `semver::Version`.\n|  version_serde | no              | `serde = \"1\"`           | Serde Deserializer and Serializer implementation for `lenient_version`.\n\n\n#### Examples\n\n##### `semver11`\n\n```toml\nlenient_semver = { version = \"*\", features = [ \"semver11\" ] }\n```\n\n```rust\nuse semver::Version as Version11;\n\n// This features is enabled by default and is usable through `parse` directly,\n// but can also be used with `parse_into`.\nlet version = lenient_semver::parse_into::\u003cVersion11\u003e(\"v1.2.3.Final\").unwrap();\nassert_eq!(version, Version11::parse(\"1.2.3+Final\").unwrap());\n```\n\n##### `semver10`\n\n```toml\nlenient_semver = { version = \"*\", features = [ \"semver10\" ] }\n```\n\n```rust\n// We have both version of semver available, the older one\n// is renamed to `semver010`.\nuse semver010::Version as Version10;\n\n// The default parse is fixed to the latest semver::Version,\n// so we need to use `parse_into`.\nlet version = lenient_semver::parse_into::\u003cVersion10\u003e(\"v1.2.3.Final\").unwrap();\nassert_eq!(version, Version10::parse(\"1.2.3+Final\").unwrap());\n```\n\n##### `version_lite`\n\n```toml\nlenient_semver = { version = \"*\", features = [ \"version_lite\" ] }\n```\n\nWith this features, lenient_semver now comes with it's own version.\nThat particular implementation supports numbers beyond patch directly.\nNote that lenient_semver still parses those additional number without complaining,\nbut they are added as build attribute to semver Versions.\n\n```rust\nuse lenient_semver::Version;\n\nlet version = lenient_semver::parse_into::\u003cVersion\u003e(\"1.3.3.7\").unwrap();\nassert_eq!(version, Version::parse(\"1.3.3.7\").unwrap()); // Version::parse delegates to this parser\n```\n\nThe native support allows such version to be compared properly, which does not work with semver.\n\n```rust\nuse lenient_semver::Version;\n\nlet version_a = Version::parse(\"1.3.3.7\").unwrap();\nlet version_b = Version::parse(\"1.3.3.8\").unwrap();\nassert!(version_a \u003c version_b);\n\n// with semver, that fails:\nlet version_a = lenient_semver::parse(\"1.3.3.7\").unwrap();\nlet version_b = lenient_semver::parse(\"1.3.3.8\").unwrap();\nassert_eq!(version_a \u003c version_b, false);\nassert_eq!(version_a, version_b);\n```\n\nFurthermore, `Version` does not own the data for the metadata identifiers.\nThe metadata can be disassociated, so the version can reference a different owner.\n\n```rust\nuse lenient_semver::{Version, VersionBuilder};\n\nlet input = \"1.3.3.7-beta.21+build.42\";\n// make an owned copy, so we don't cheat by using the 'static lifetime.\nlet input = String::from(input);\n\n// This version references slices from the `input` String\nlet version = lenient_semver::parse_into::\u003cVersion\u003e(input.as_ref()).unwrap();\n\n// Which prevents us from dropping the input\n// drop(input);\n\n// We can disassociate the metadata, which allows the new version to reference something else\nlet (mut version, pre, build) = version.disassociate_metadata();\n\n// We still get the referenced input slices, so we create owned copies\nlet pre: Vec\u003cString\u003e = pre.into_iter().map(ToOwned::to_owned).collect();\nlet build: Vec\u003cString\u003e = build.into_iter().map(ToOwned::to_owned).collect();\n\n// now we can safely drop the input\ndrop(input);\n\n// We can also re-add the cloned identifiers.\n// The version would now be bound to the lifetime of this method.\n// Just for fun, we swap pre-release and build\nfor pre in \u0026pre {\n    version.add_build(pre.as_ref());\n}\nfor build in \u0026build {\n    version.add_pre_release(build.as_ref());\n}\n\nassert_eq!(\"1.3.3.7-build.42+beta.21\".to_string(), version.to_string());\n```\n\n##### `version_semver`\n\n```toml\nlenient_semver = { version = \"*\", features = [ \"version_semver\" ] }\n```\n\nIf you need to store an owned copy of the version information, you should copy into `semver::Version` or your custom version type instead.\nIf you only ever intend to store the version information, it might make more sense to parse directly into `semver::Version` instead.\n\n```rust\nuse semver::Version;\n\nlet input = String::from(\"v1.3.3.7-beta-21+build-42\");\nlet version = lenient_semver::Version::parse(\u0026input).unwrap();\nlet version = Version::from(version);\nassert_eq!(\"1.3.3-beta.21+7.build.42\", \u0026version.to_string());\n```\n\n##### `version_serde`\n\n```toml\nlenient_semver = { version = \"*\", features = [ \"version_serde\" ] }\n```\n\nThis feature also enabled `version_lite` and brings serde support for the own Version type.\nSince `lenient_semver::Version` does not take ownership of the metadata identifiers,\nthe lifetime of the deserialization result is bound to the input.\n\n```rust\nuse lenient_semver::{Version, VersionBuilder};\nuse serde::Deserialize;\n\n#[derive(Debug, Deserialize)]\nstruct DependencySpec\u003c'input\u003e {\n    /// Refer to name as owned value\n    name: String,\n    /// Borrows from the input string\n    #[serde(borrow)]\n    version: Version\u003c'input\u003e,\n}\n\nlet input = \"\n    {\n        \\\"name\\\": \\\"lenient_semver\\\",\n        \\\"version\\\": \\\"1.3.3.7+build.42\\\"\n    }\";\n// make an owned copy, so we don't cheat by using the 'static lifetime.\nlet input = String::from(input);\n\n// use serde as one would normally do\nlet dep: DependencySpec = serde_json::from_str(input.as_ref()).unwrap();\nprintln!(\"{:?}\", dep);\n\n// cannot move out of `input` because it is borrowed\n// drop(input);\n\nlet mut expected = Version::new(1, 3, 3);\nexpected.add_additional(7);\nexpected.add_build(\"build\");\nexpected.add_build(\"42\");\n\nassert_eq!(dep.version, expected);\n\n// now we can drop the input\ndrop(input);\n```\n\n##### `parse_partial`\n\n```toml\nlenient_semver = { version = \"*\", features = [ \"parse_partial\" ] }\n```\n\nThis feature enables the `partial` feature of the parser.\nThe partial parser will not try to consume all input.\nInstead it parses the version as far as possible and will return the unconsumed input alongside the parsed version.\n\n```rust\nuse lenient_semver::{Version, VersionBuilder, parser};\nlet input = \"1.2.3   42+build 1.3.3.7 // end\";\n\n// parse first version\nlet (version, remainder) = parser::parse_partial::\u003cVersion\u003e(input).unwrap();\nlet expected = Version::new(1, 2, 3);\nassert_eq!(version, expected);\n// trailing whitespace is considered part of a version and consumed as well\nassert_eq!(\"42+build 1.3.3.7 // end\", remainder);\n\n// parse second version\nlet (version, remainder) = parser::parse_partial::\u003cVersion\u003e(remainder).unwrap();\nlet mut expected = Version::new(42, 0, 0);\nexpected.add_build(\"build\");\nassert_eq!(version, expected);\nassert_eq!(\"1.3.3.7 // end\", remainder);\n\n// parse last version\nlet (version, remainder) = parser::parse_partial::\u003cVersion\u003e(remainder).unwrap();\nlet mut expected = Version::new(1, 3, 3);\nexpected.add_additional(7);\nassert_eq!(version, expected);\nassert_eq!(\"// end\", remainder);\n\n// parse partial still expects to parse something.\n// It will fail with `UnexpectedInput` or `MissingMajorNumber` if the input does not match at least a major version.\n// let's try to parse the remaining input\nlet error = parser::parse_partial::\u003cVersion\u003e(remainder).unwrap_err();\nassert_eq!(error.error_kind(), parser::ErrorKind::UnexpectedInput);\nassert_eq!(error.error_line(), \"Unexpected `/`\");\n\n// or an empty string\nlet error = parser::parse_partial::\u003cVersion\u003e(\"         \").unwrap_err();\nassert_eq!(error.error_kind(), parser::ErrorKind::MissingMajorNumber);\nassert_eq!(\n    error.error_line(),\n    \"Could not parse the major identifier: No input\"\n);\n\n// The rules of when a certain number will be parsed are even more relaxed\nlet (version, remainder) = parser::parse_partial::\u003cVersion\u003e(\"1foobar\").unwrap();\nlet expected = Version::new(1, 0, 0);\nassert_eq!(version, expected);\nassert_eq!(remainder, \"foobar\");\n\n// Furthermore, the characters `*` and `?` are allowed to appear everywhere where other alphabetic character are allowed.\n// This relaxes the rule that only a-z, A-Z, and 0-9 are allowed.\n// Those characters have no special meaning and will be parsed as pre-release or build segment.\nlet (version, remainder) = parser::parse_partial::\u003cVersion\u003e(\"1.2.*+final?\").unwrap();\nlet mut expected = Version::new(1, 2, 0);\nexpected.add_pre_release(\"*\");\nexpected.add_build(\"final?\");\nassert_eq!(version, expected);\nassert_eq!(remainder, \"\");\n```\n\nLicense: MIT OR Apache-2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fknutwalker%2Flenient-semver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fknutwalker%2Flenient-semver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fknutwalker%2Flenient-semver/lists"}