Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(js): remove double wrap #1138

Merged
merged 2 commits into from
Mar 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 1 addition & 27 deletions js-rattler/crate/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
mod error;
mod gateway;
mod parse_strictness;
pub mod solve;
mod utils;
mod version;
mod version_spec;

pub use error::{JsError, JsResult};
use rattler_conda_types::ParseStrictness;

use wasm_bindgen::prelude::*;

Expand All @@ -21,29 +21,3 @@ static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
pub fn start() {
utils::set_panic_hook();
}

/// Defines how strict a parser should be when parsing an object from a string.
/// @public
#[wasm_bindgen(js_name=ParseStrictness)]
pub enum JsParseStrictness {
Strict,
Lenient,
}

impl From<ParseStrictness> for JsParseStrictness {
fn from(value: ParseStrictness) -> Self {
match value {
ParseStrictness::Strict => JsParseStrictness::Strict,
ParseStrictness::Lenient => JsParseStrictness::Lenient,
}
}
}

impl From<JsParseStrictness> for ParseStrictness {
fn from(value: JsParseStrictness) -> Self {
match value {
JsParseStrictness::Strict => ParseStrictness::Strict,
JsParseStrictness::Lenient => ParseStrictness::Lenient,
}
}
}
37 changes: 37 additions & 0 deletions js-rattler/crate/parse_strictness.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use rattler_conda_types::ParseStrictness;
use serde::Deserialize;
use wasm_bindgen::prelude::wasm_bindgen;

#[wasm_bindgen(typescript_custom_section)]
const PARSE_STRICTNESS_TS: &'static str = r#"
/**
* Defines how strict a parser should be when parsing an object from a string.
*
* @public
*/
export type ParseStrictness = "strict" | "lenient";
"#;

#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_name = "ParseStrictness", typescript_type = "ParseStrictness")]
pub type JsParseStrictness;
}

impl TryFrom<JsParseStrictness> for ParseStrictness {
type Error = serde_wasm_bindgen::Error;

fn try_from(value: JsParseStrictness) -> Result<Self, Self::Error> {
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum EnumNames {
Strict,
Lenient,
}

match serde_wasm_bindgen::from_value(value.obj)? {
EnumNames::Strict => Ok(ParseStrictness::Strict),
EnumNames::Lenient => Ok(ParseStrictness::Lenient),
}
}
}
157 changes: 128 additions & 29 deletions js-rattler/crate/version.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
use crate::JsResult;
use rattler_conda_types::{Version, VersionBumpType};
use std::{cmp::Ordering, str::FromStr};
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::JsValue;

use crate::JsResult;

#[wasm_bindgen]
/// This class implements an order relation between version strings. Version
/// strings can contain the usual alphanumeric characters (A-Za-z0-9), separated
/// into segments by dots and underscores. Empty segments (i.e. two consecutive
/// dots, a leading/trailing underscore) are not permitted. An optional epoch
/// number - an integer followed by '!' - can precede the actual version string
/// (this is useful to indicate a change in the versioning scheme itself).
/// Version comparison is case-insensitive.
///
/// @public
#[wasm_bindgen(js_name = "Version")]
#[repr(transparent)]
#[derive(Eq, PartialEq)]
pub struct JsVersion {
Expand All @@ -28,110 +39,198 @@ impl AsRef<Version> for JsVersion {
}
}

#[wasm_bindgen]
pub struct MajorMinor(pub usize, pub usize);

#[wasm_bindgen]
#[wasm_bindgen(js_class = "Version")]
impl JsVersion {
/// Constructs a new Version object from a string representation.
#[wasm_bindgen(constructor)]
pub fn new(version: &str) -> JsResult<Self> {
pub fn new(
#[wasm_bindgen(param_description = "The string representation of the version.")]
version: &str,
) -> JsResult<Self> {
let version = Version::from_str(version)?;
Ok(version.into())
}

/// Returns the string representation of the version.
///
/// An attempt is made to return the version in the same format as the input
/// string but this is not guaranteed.
#[wasm_bindgen(js_name = "toString")]
pub fn as_str(&self) -> String {
format!("{}", self.as_ref())
}

/// The epoch part of the version. E.g. `1` in `1!2.3`.
#[wasm_bindgen(getter)]
pub fn epoch(&self) -> Option<usize> {
self.as_ref().epoch_opt().map(|v| v as usize)
}

#[wasm_bindgen(getter)]
/// `true` if the version has a local part. E.g. `2.3` in `1+2.3`.
#[wasm_bindgen(getter, js_name = "hasLocal")]
pub fn has_local(&self) -> bool {
self.as_ref().has_local()
}

#[wasm_bindgen(getter)]
/// `true` if the version is considered a development version.
///
/// A development version is a version that contains the string `dev` in the
/// version string.
#[wasm_bindgen(getter, js_name = "isDev")]
pub fn is_dev(&self) -> bool {
self.as_ref().is_dev()
}

pub fn as_major_minor(&self) -> Option<MajorMinor> {
/// Returns the major and minor part of the version if the version does not
/// represent a typical major minor version. If any of the parts are not a
/// single number, undefined is returned.
// TODO: Simplify when https://github.com/rustwasm/wasm-bindgen/issues/122 is fixed
#[wasm_bindgen(
js_name = "asMajorMinor",
unchecked_return_type = "[number, number] | undefined"
)]
pub fn as_major_minor(&self) -> Option<Vec<JsValue>> {
let (major, minor) = self.as_ref().as_major_minor()?;
Some(MajorMinor(major as _, minor as _))
}

pub fn starts_with(&self, other: &Self) -> bool {
Some(vec![
JsValue::from(major as usize),
JsValue::from(minor as usize),
])
}

/// Returns true if this version starts with the other version. This is
/// defined as the other version being a prefix of this version.
#[wasm_bindgen(js_name = "startsWith")]
pub fn starts_with(
&self,
#[wasm_bindgen(param_description = "The version to use for the comparison")] other: &Self,
) -> bool {
self.as_ref().starts_with(other.as_ref())
}

pub fn compatible_with(&self, other: &Self) -> bool {
/// Returns true if this version is compatible with the other version.
#[wasm_bindgen(js_name = "compatibleWith")]
pub fn compatible_with(
&self,
#[wasm_bindgen(param_description = "The version to use for the comparison")] other: &Self,
) -> bool {
self.as_ref().compatible_with(other.as_ref())
}

pub fn pop_segments(&self, n: usize) -> Option<Self> {
/// Pop the last `n` segments from the version.
#[wasm_bindgen(js_name = "popSegments")]
pub fn pop_segments(
&self,
#[wasm_bindgen(param_description = "The number of segments to pop")] n: usize,
) -> Option<Self> {
Some(self.as_ref().pop_segments(n)?.into())
}

pub fn extend_to_length(&self, length: usize) -> JsResult<Self> {
/// Extend the version to the given length by adding zeros. If the version
/// is already at the specified length or longer the original version
/// will be returned.
#[wasm_bindgen(js_name = "extendToLength")]
pub fn extend_to_length(
&self,
#[wasm_bindgen(param_description = "The length to extend to")] length: usize,
) -> JsResult<Self> {
Ok(self.as_ref().extend_to_length(length)?.into_owned().into())
}

pub fn with_segments(&self, start: usize, stop: usize) -> Option<Self> {
let range = start..stop;
/// Returns a new version with the segments from start to end (exclusive).
///
/// Returns undefined if the start or end index is out of bounds.
#[wasm_bindgen(js_name = "withSegments")]
pub fn with_segments(
&self,
#[wasm_bindgen(param_description = "The start index")] start: usize,
#[wasm_bindgen(param_description = "The end index")] end: usize,
) -> Option<Self> {
let range = start..end;
Some(self.as_ref().with_segments(range)?.into())
}

/// The number of segments in the version.
#[wasm_bindgen(getter)]
pub fn length(&self) -> usize {
self.as_ref().segment_count()
}

/// Create a new version with local segment stripped.
/// Returns the version without the local part. E.g. `1+2.3` becomes `1`.
#[wasm_bindgen(js_name = "stripLocal")]
pub fn strip_local(&self) -> Self {
self.as_ref().strip_local().into_owned().into()
}

/// Returns a new version where the major segment of this version has been bumped.
/// Returns a new version where the major segment of this version has been
/// bumped.
#[wasm_bindgen(js_name = "bumpMajor")]
pub fn bump_major(&self) -> JsResult<Self> {
Ok(self.as_ref().bump(VersionBumpType::Major).map(Into::into)?)
}

/// Returns a new version where the minor segment of this version has been bumped.
/// Returns a new version where the minor segment of this version has been
/// bumped.
#[wasm_bindgen(js_name = "bumpMinor")]
pub fn bump_minor(&self) -> JsResult<Self> {
Ok(self.as_ref().bump(VersionBumpType::Minor).map(Into::into)?)
}

/// Returns a new version where the patch segment of this version has been bumped.
/// Returns a new version where the patch segment of this version has been
/// bumped.
#[wasm_bindgen(js_name = "bumpPatch")]
pub fn bump_patch(&self) -> JsResult<Self> {
Ok(self.as_ref().bump(VersionBumpType::Patch).map(Into::into)?)
}

/// Returns a new version where the last segment of this version has been bumped.
/// Returns a new version where the last segment of this version has been
/// bumped.
#[wasm_bindgen(js_name = "bumpLast")]
pub fn bump_last(&self) -> JsResult<Self> {
Ok(self.as_ref().bump(VersionBumpType::Last).map(Into::into)?)
}

/// Returns a new version where the given segment of this version has been bumped.
pub fn bump_segment(&self, index: i32) -> JsResult<Self> {
/// Returns a new version where the given segment of this version has been
/// bumped.
#[wasm_bindgen(js_name = "bumpSegment")]
pub fn bump_segment(
&self,
#[wasm_bindgen(param_description = "The index of the segment to bump")] index: i32,
) -> JsResult<Self> {
Ok(self
.as_ref()
.bump(VersionBumpType::Segment(index))
.map(Into::into)?)
}

/// Returns a new version where the last segment is an "alpha" segment (ie. `.0a0`)
/// Returns a new version where the last segment is an "alpha" segment (ie.
/// `.0a0`)
#[wasm_bindgen(js_name = "withAlpha")]
pub fn with_alpha(&self) -> Self {
self.as_ref().with_alpha().into_owned().into()
}

pub fn equals(&self, other: &Self) -> bool {
/// Compares this version with another version. Returns `true` if the
/// versions are considered equal.
///
/// Note that two version strings can be considered equal even if they are
/// not exactly the same. For example, `1.0` and `1` are considered equal.
#[wasm_bindgen(js_name = "equals")]
pub fn equals(
&self,
#[wasm_bindgen(param_description = "The version to compare with")] other: &Self,
) -> bool {
self.as_ref() == other.as_ref()
}

pub fn compare(&self, other: &Self) -> i8 {
/// Compare two versions.
///
/// Returns `-1` if this instance should be ordered before `other`, `0` if
/// this version and `other` are considered equal, `1` if this version
/// should be ordered after `other`.
pub fn compare(
&self,
#[wasm_bindgen(param_description = "The version to compare with")] other: &Self,
) -> i8 {
match self.as_ref().cmp(other.as_ref()) {
Ordering::Less => -1,
Ordering::Equal => 0,
Expand Down
37 changes: 29 additions & 8 deletions js-rattler/crate/version_spec.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
use crate::version::JsVersion;
use crate::{JsParseStrictness, JsResult};
use rattler_conda_types::VersionSpec;
use rattler_conda_types::{ParseStrictness, VersionSpec};
use wasm_bindgen::prelude::wasm_bindgen;

#[wasm_bindgen]
use crate::parse_strictness::JsParseStrictness;
use crate::{version::JsVersion, JsResult};

/// Represents a version specification in the conda ecosystem.
///
/// @public
#[wasm_bindgen(js_name = "VersionSpec")]
#[repr(transparent)]
#[derive(Eq, PartialEq)]
pub struct JsVersionSpec {
Expand All @@ -28,19 +32,36 @@ impl AsRef<VersionSpec> for JsVersionSpec {
}
}

#[wasm_bindgen]
#[wasm_bindgen(js_class = "VersionSpec")]
impl JsVersionSpec {
/// Constructs a new VersionSpec object from a string representation.
#[wasm_bindgen(constructor)]
pub fn new(version_spec: &str, parse_strictness: JsParseStrictness) -> JsResult<Self> {
let spec = VersionSpec::from_str(version_spec, parse_strictness.into())?;
pub fn new(
#[wasm_bindgen(param_description = "The string representation of the version spec.")]
version_spec: &str,
#[wasm_bindgen(param_description = "The strictness of the parser.")]
parse_strictness: Option<JsParseStrictness>,
) -> JsResult<Self> {
let parse_strictness = parse_strictness
.map(TryFrom::try_from)
.transpose()?
.unwrap_or(ParseStrictness::Lenient);

let spec = VersionSpec::from_str(version_spec, parse_strictness)?;
Ok(spec.into())
}

/// Returns the string representation of the version spec.
#[wasm_bindgen(js_name = "toString")]
pub fn as_str(&self) -> String {
format!("{}", self.as_ref())
}

pub fn matches(&self, version: &JsVersion) -> bool {
/// Returns true if the version matches this version spec.
pub fn matches(
&self,
#[wasm_bindgen(param_description = "The version to match")] version: &JsVersion,
) -> bool {
self.as_ref().matches(version.as_ref())
}
}
Loading