Repository: typst/typst
File: crates/typst/src/foundations/int.rs (Rust)

Code before:
        };
        Bytes::from(array.to_vec())
    }
}

impl Repr for i64 {
    fn repr(&self) -> EcoString {
        eco_format!("{:?}", self)
    }
}

/// The endianness of a bytes to and from int conversion.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
enum Endianness {
    Big,
    Little,
}

cast! {
    Endianness,
    v: Str =>
        match v.as_str() {
            "big" => Ok(Self::Big),
            "little" => Ok(Self::Little),
            _ => Err("invalid endianness"),
        }?,
}

/// A value that can be cast to an integer.
pub struct ToInt(i64);

cast! {
    ToInt,
    v: i64 => Self(v),
    v: bool => Self(v as i64),
    v: f64 => Self(v as i64),
    v: Str => Self(parse_int(&v).map_err(|_| eco_format!("invalid integer: {}", v))?),
}

fn parse_int(mut s: &str) -> Result<i64, ParseIntError> {
    let mut sign = 1;
    if let Some(rest) = s.strip_prefix('-').or_else(|| s.strip_prefix(repr::MINUS_SIGN)) {
        sign = -1;
        s = rest;
    }
    if sign == -1 && s == "9223372036854775808" {
        return Ok(i64::MIN);
    }
    Ok(sign * s.parse::<i64>()?)
}


Review comment (suggestion):
~~I think we can implement `FromStr` for `Endianness`, and call `Endianness::from_str` here. Using `FromStr` here might be more friendly to developers.~~

EDIT: Please see laurmaedje's suggestion below.

Code after:
        let (rest, decimal) = match endian {
            Endianness::Big => buf.split_at_mut(8 - len),
            Endianness::Little => {
                let (first, second) = buf.split_at_mut(len);
                (second, first)
            }
        };

        decimal.copy_from_slice(bytes.as_ref());

        // Perform sign-extension if necessary.
        if signed {
            let most_significant_byte = match endian {
                Endianness::Big => decimal[0],
                Endianness::Little => decimal[len - 1],
            };

            if most_significant_byte & 0b1000_0000 != 0 {
                rest.fill(0xFF);
            }
        }

        Ok(match endian {
            Endianness::Big => i64::from_be_bytes(buf),
            Endianness::Little => i64::from_le_bytes(buf),
        })
    }

    /// Converts an integer to bytes.
    ///
    /// ```example
    /// #array(10000.to-bytes(endian: "big")) \
    /// #array(10000.to-bytes(size: 4))
    /// ```
    #[func]
    pub fn to_bytes(
        self,
        /// The endianness of the conversion.
        #[named]
        #[default(Endianness::Little)]
        endian: Endianness,
        /// The size in bytes of the resulting bytes (must be at least zero). If
        /// the integer is too large to fit in the specified size, the
        /// conversion will truncate the remaining bytes based on the
        /// endianness. To keep the same resulting value, if the endianness is
        /// big-endian, the truncation will happen at the rightmost bytes.
        /// Otherwise, if the endianness is little-endian, the truncation will
        /// happen at the leftmost bytes.
        ///
        /// Be aware that if the integer is negative and the size is not enough
        /// to make the number fit, when passing the resulting bytes to