Skip to content
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
194 changes: 194 additions & 0 deletions crates/toml_writer/src/integer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
use core::fmt::{self, Display};

/// Describes how a TOML integer should be formatted.
#[derive(Copy, Clone, Debug)]
pub struct TomlIntegerFormat {
radix: Radix,
}

impl TomlIntegerFormat {
/// Creates a new integer format (decimal).
pub fn new() -> Self {
Self {
radix: Radix::Decimal,
}
}

/// Sets the format to decimal.
pub fn as_decimal(mut self) -> Self {
self.radix = Radix::Decimal;
self
}

/// Sets the format to hexadecimal with all characters in uppercase.
pub fn as_hex_upper(mut self) -> Self {
self.radix = Radix::Hexadecimal {
case: HexCase::Upper,
};
self
}

/// Sets the format to hexadecimal with all characters in lowercase.
pub fn as_hex_lower(mut self) -> Self {
self.radix = Radix::Hexadecimal {
case: HexCase::Lower,
};
self
}

/// Sets the format to octal.
pub fn as_octal(mut self) -> Self {
self.radix = Radix::Octal;
self
}

/// Sets the format to binary.
pub fn as_binary(mut self) -> Self {
self.radix = Radix::Binary;
self
}

/// Formats `value` as a TOML integer.
///
/// Returns `None` if the value cannot be formatted
/// (e.g. value is negative and the radix is not decimal).
pub fn format<N: PartialOrd<i32>>(self, value: N) -> Option<TomlInteger<N>>
where
TomlInteger<N>: crate::WriteTomlValue,
{
match self.radix {
Radix::Decimal => (),
Radix::Hexadecimal { .. } | Radix::Octal | Radix::Binary => {
if value < 0 {
return None;
}
}
}

Some(TomlInteger {
value,
format: self,
})
}
}

impl Default for TomlIntegerFormat {
fn default() -> Self {
Self::new()
}
}

/// Helper struct for formatting TOML integers.
///
/// This may be constructed by calling [`TomlIntegerFormat::format()`].
#[derive(Copy, Clone, Debug)]
pub struct TomlInteger<N> {
value: N,
format: TomlIntegerFormat,
}

impl crate::WriteTomlValue for TomlInteger<u8> {
fn write_toml_value<W: crate::TomlWrite + ?Sized>(&self, writer: &mut W) -> fmt::Result {
write_toml_value(self.value, &self.format, writer)
}
}

impl crate::WriteTomlValue for TomlInteger<i8> {
fn write_toml_value<W: crate::TomlWrite + ?Sized>(&self, writer: &mut W) -> fmt::Result {
write_toml_value(self.value, &self.format, writer)
}
}

impl crate::WriteTomlValue for TomlInteger<u16> {
fn write_toml_value<W: crate::TomlWrite + ?Sized>(&self, writer: &mut W) -> fmt::Result {
write_toml_value(self.value, &self.format, writer)
}
}

impl crate::WriteTomlValue for TomlInteger<i16> {
fn write_toml_value<W: crate::TomlWrite + ?Sized>(&self, writer: &mut W) -> fmt::Result {
write_toml_value(self.value, &self.format, writer)
}
}

impl crate::WriteTomlValue for TomlInteger<u32> {
fn write_toml_value<W: crate::TomlWrite + ?Sized>(&self, writer: &mut W) -> fmt::Result {
write_toml_value(self.value, &self.format, writer)
}
}

impl crate::WriteTomlValue for TomlInteger<i32> {
fn write_toml_value<W: crate::TomlWrite + ?Sized>(&self, writer: &mut W) -> fmt::Result {
write_toml_value(self.value, &self.format, writer)
}
}

impl crate::WriteTomlValue for TomlInteger<u64> {
fn write_toml_value<W: crate::TomlWrite + ?Sized>(&self, writer: &mut W) -> fmt::Result {
write_toml_value(self.value, &self.format, writer)
}
}

impl crate::WriteTomlValue for TomlInteger<i64> {
fn write_toml_value<W: crate::TomlWrite + ?Sized>(&self, writer: &mut W) -> fmt::Result {
write_toml_value(self.value, &self.format, writer)
}
}

impl crate::WriteTomlValue for TomlInteger<u128> {
fn write_toml_value<W: crate::TomlWrite + ?Sized>(&self, writer: &mut W) -> fmt::Result {
write_toml_value(self.value, &self.format, writer)
}
}

impl crate::WriteTomlValue for TomlInteger<i128> {
fn write_toml_value<W: crate::TomlWrite + ?Sized>(&self, writer: &mut W) -> fmt::Result {
write_toml_value(self.value, &self.format, writer)
}
}

impl crate::WriteTomlValue for TomlInteger<usize> {
fn write_toml_value<W: crate::TomlWrite + ?Sized>(&self, writer: &mut W) -> fmt::Result {
write_toml_value(self.value, &self.format, writer)
}
}

impl crate::WriteTomlValue for TomlInteger<isize> {
fn write_toml_value<W: crate::TomlWrite + ?Sized>(&self, writer: &mut W) -> fmt::Result {
write_toml_value(self.value, &self.format, writer)
}
}

#[derive(Copy, Clone, Debug)]
enum Radix {
Decimal,
Hexadecimal { case: HexCase },
Octal,
Binary,
}

#[derive(Copy, Clone, Debug)]
enum HexCase {
Upper,
Lower,
}

fn write_toml_value<
N: Display + fmt::UpperHex + fmt::LowerHex + fmt::Octal + fmt::Binary,
W: crate::TomlWrite + ?Sized,
>(
value: N,
format: &TomlIntegerFormat,
writer: &mut W,
) -> fmt::Result {
match format.radix {
Radix::Decimal => write!(writer, "{value}")?,
Radix::Hexadecimal { case } => match case {
HexCase::Upper => write!(writer, "0x{value:X}")?,
HexCase::Lower => write!(writer, "0x{value:x}")?,
},
Radix::Octal => write!(writer, "0o{value:o}")?,
Radix::Binary => write!(writer, "0b{value:b}")?,
}
Ok(())
}
3 changes: 3 additions & 0 deletions crates/toml_writer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,14 @@
#[cfg(feature = "alloc")]
extern crate alloc;

mod integer;
mod key;
mod string;
mod value;
mod write;

pub use integer::TomlInteger;
pub use integer::TomlIntegerFormat;
#[cfg(feature = "alloc")]
pub use key::ToTomlKey;
pub use key::WriteTomlKey;
Expand Down
99 changes: 99 additions & 0 deletions crates/toml_writer/tests/integer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#![cfg(feature = "alloc")]

use snapbox::prelude::*;
use snapbox::str;

use toml_writer::ToTomlValue;
use toml_writer::TomlInteger;
use toml_writer::TomlIntegerFormat;
use toml_writer::WriteTomlValue;

#[track_caller]
fn t<N: Copy + core::fmt::Debug + PartialOrd<i32>>(value: N, expected: impl IntoData)
where
TomlInteger<N>: WriteTomlValue,
{
let results = IntegerResults {
value,
decimal: TomlIntegerFormat::new()
.as_decimal()
.format(value)
.map(|i| i.to_toml_value()),
hex_upper: TomlIntegerFormat::new()
.as_hex_upper()
.format(value)
.map(|i| i.to_toml_value()),
hex_lower: TomlIntegerFormat::new()
.as_hex_lower()
.format(value)
.map(|i| i.to_toml_value()),
octal: TomlIntegerFormat::new()
.as_octal()
.format(value)
.map(|i| i.to_toml_value()),
binary: TomlIntegerFormat::new()
.as_binary()
.format(value)
.map(|i| i.to_toml_value()),
};
snapbox::assert_data_eq!(results.to_debug(), expected.raw());
}

#[derive(Debug)]
#[allow(dead_code)]
struct IntegerResults<N: core::fmt::Debug> {
value: N,
decimal: Option<String>,
hex_upper: Option<String>,
hex_lower: Option<String>,
octal: Option<String>,
binary: Option<String>,
}

#[test]
fn positive() {
t(
42,
str![[r#"
IntegerResults {
value: 42,
decimal: Some(
"42",
),
hex_upper: Some(
"0x2A",
),
hex_lower: Some(
"0x2a",
),
octal: Some(
"0o52",
),
binary: Some(
"0b101010",
),
}

"#]],
);
}

#[test]
fn negative() {
t(
-42,
str![[r#"
IntegerResults {
value: -42,
decimal: Some(
"-42",
),
hex_upper: None,
hex_lower: None,
octal: None,
binary: None,
}

"#]],
);
}