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

Add Support for Dot Case & Shouty Dot Case #44

Closed
wants to merge 6 commits into from
Closed
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ indicators are dropped, except insofar as CamelCase capitalizes the first word.
6. Title Case
7. SHOUTY-KEBAB-CASE
8. Train-Case
9. dot.case
10. SHOUTY.DOT.CASE

## MSRV

Expand Down
87 changes: 87 additions & 0 deletions src/dot.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use std::fmt;

use crate::{lowercase, transform};

/// This trait defines a dot case conversion.
///
/// In dot.case, word boundaries are indicated by dots.
///
/// ## Example:
///
/// ```rust
/// use heck::ToDotCase;
///
/// let sentence = "We carry a new world here, in our hearts.";
/// assert_eq!(sentence.to_dot_case(), "we.carry.a.new.world.here.in.our.hearts");
/// ```
pub trait ToDotCase: ToOwned {
/// Convert this type to dot case.
fn to_dot_case(&self) -> Self::Owned;
}

impl ToDotCase for str {
fn to_dot_case(&self) -> String {
AsDotCase(self).to_string()
}
}

/// This wrapper performs a dot case conversion in [`fmt::Display`].
///
/// ## Example:
///
/// ```
/// use heck::AsDotCase;
///
/// let sentence = "We carry a new world here, in our hearts.";
/// assert_eq!(format!("{}", AsDotCase(sentence)), "we.carry.a.new.world.here.in.our.hearts");
/// ```
pub struct AsDotCase<T: AsRef<str>>(pub T);

impl<T: AsRef<str>> fmt::Display for AsDotCase<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
transform(self.0.as_ref(), lowercase, |f| write!(f, "."), f)
}
}

#[cfg(test)]
mod tests {
use super::ToDotCase;

macro_rules! t {
($t:ident : $s1:expr => $s2:expr) => {
#[test]
fn $t() {
assert_eq!($s1.to_dot_case(), $s2)
}
};
}

t!(test1: "CamelCase" => "camel.case");
t!(test2: "This is Human case." => "this.is.human.case");
t!(test3: "MixedUP CamelCase, with some Spaces" => "mixed.up.camel.case.with.some.spaces");
t!(test4: "mixed_up_ snake_case with some _spaces" => "mixed.up.snake.case.with.some.spaces");
t!(test5: "kebab-case" => "kebab.case");
t!(test6: "SHOUTY_SNAKE_CASE" => "shouty.snake.case");
t!(test7: "snake_case" => "snake.case");
t!(test8: "this-contains_ ALLKinds OfWord_Boundaries" => "this.contains.all.kinds.of.word.boundaries");
#[cfg(feature = "unicode")]
t!(test9: "XΣXΣ baffle" => "xσxς.baffle");
t!(test10: "XMLHttpRequest" => "xml.http.request");
t!(test11: "FIELD_NAME11" => "field.name11");
t!(test12: "99BOTTLES" => "99bottles");
t!(test13: "FieldNamE11" => "field.nam.e11");
t!(test14: "abc123def456" => "abc123def456");
t!(test16: "abc123DEF456" => "abc123.def456");
t!(test17: "abc123Def456" => "abc123.def456");
t!(test18: "abc123DEf456" => "abc123.d.ef456");
t!(test19: "ABC123def456" => "abc123def456");
t!(test20: "ABC123DEF456" => "abc123def456");
t!(test21: "ABC123Def456" => "abc123.def456");
t!(test22: "ABC123DEf456" => "abc123d.ef456");
t!(test23: "ABC123dEEf456FOO" => "abc123d.e.ef456.foo");
t!(test24: "abcDEF" => "abc.def");
t!(test25: "ABcDE" => "a.bc.de");
t!(test26: "dot_case" => "dot.case");
t!(test27: "SHOUTY_DOT_CASE" => "shouty.dot.case");
t!(test28: "mixed.up. dot.case with some .spaces" => "mixed.up.dot.case.with.some.spaces");
}
4 changes: 4 additions & 0 deletions src/kebab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,8 @@ mod tests {
// Japanese and Chinese do not have word separation.
t!(test12: "ファイルを読み込み" => "ファイルを読み込み");
t!(test13: "祝你一天过得愉快" => "祝你一天过得愉快");
// --
t!(test14: "dot.case" => "dot-case");
t!(test15: "SHOUTY.DOT.CASE" => "shouty-dot-case");
t!(test16: "mixed.up. dot.case with some .spaces" => "mixed-up-dot-case-with-some-spaces");
}
8 changes: 8 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
//! 6. Title Case
//! 7. SHOUTY-KEBAB-CASE
//! 8. Train-Case
//! 9. dot.case
//! 10. SHOUTY.DOT.CASE
#![deny(missing_docs)]
#![forbid(unsafe_code)]
#![no_std]
Expand All @@ -47,10 +49,14 @@ mod lower_camel;
mod shouty_kebab;
mod shouty_snake;
mod snake;
mod shouty_dot;
mod dot;
mod title;
mod train;
mod upper_camel;



pub use kebab::{AsKebabCase, ToKebabCase};
pub use lower_camel::{AsLowerCamelCase, ToLowerCamelCase};
pub use shouty_kebab::{AsShoutyKebabCase, ToShoutyKebabCase};
Expand All @@ -63,6 +69,8 @@ pub use train::{AsTrainCase, ToTrainCase};
pub use upper_camel::{
AsUpperCamelCase, AsUpperCamelCase as AsPascalCase, ToPascalCase, ToUpperCamelCase,
};
pub use dot::{AsDotCase, ToDotCase};
pub use shouty_dot::{AsShoutyDotCase, ToShoutyDotCase};

use core::fmt;

Expand Down
4 changes: 4 additions & 0 deletions src/lower_camel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,8 @@ mod tests {
t!(test8: "this-contains_ ALLKinds OfWord_Boundaries" => "thisContainsAllKindsOfWordBoundaries");
t!(test9: "XΣXΣ baffle" => "xσxςBaffle");
t!(test10: "XMLHttpRequest" => "xmlHttpRequest");
t!(test11: "dot.case" => "dotCase");
t!(test12: "SHOUTY.DOT.CASE" => "shoutyDotCase");
t!(test13: "mixed.up. dot.case with some .spaces" => "mixedUpDotCaseWithSomeSpaces");
// TODO unicode tests
}
74 changes: 74 additions & 0 deletions src/shouty_dot.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use std::fmt;

use crate::{transform, uppercase};

/// This trait defines a shouty dot case conversion.
///
/// In SHOUTY.DOT.CASE, word boundaries are indicated by dots and all
/// words are in uppercase.
///
/// ## Example:
///
/// ```rust
/// use heck::ToShoutyDotCase;
///
/// let sentence = "That world is growing in this minute.";
/// assert_eq!(sentence.to_shouty_dot_case(), "THAT.WORLD.IS.GROWING.IN.THIS.MINUTE");
/// ```
pub trait ToShoutyDotCase: ToOwned {
/// Convert this type to shouty dot case.
fn to_shouty_dot_case(&self) -> Self::Owned;
}

impl ToShoutyDotCase for str {
fn to_shouty_dot_case(&self) -> Self::Owned {
AsShoutyDotCase(self).to_string()
}
}

/// This wrapper performs a shouty dot case conversion in [`fmt::Display`].
///
/// ## Example:
///
/// ```
/// use heck::AsShoutyDotCase;
///
/// let sentence = "That world is growing in this minute.";
/// assert_eq!(format!("{}", AsShoutyDotCase(sentence)), "THAT.WORLD.IS.GROWING.IN.THIS.MINUTE");
/// ```
pub struct AsShoutyDotCase<T: AsRef<str>>(pub T);

impl<T: AsRef<str>> fmt::Display for AsShoutyDotCase<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
transform(self.0.as_ref(), uppercase, |f| write!(f, "."), f)
}
}

#[cfg(test)]
mod tests {
use super::ToShoutyDotCase;

macro_rules! t {
($t:ident : $s1:expr => $s2:expr) => {
#[test]
fn $t() {
assert_eq!($s1.to_shouty_dot_case(), $s2)
}
};
}

t!(test1: "CamelCase" => "CAMEL.CASE");
t!(test2: "This is Human case." => "THIS.IS.HUMAN.CASE");
t!(test3: "MixedUP CamelCase, with some Spaces" => "MIXED.UP.CAMEL.CASE.WITH.SOME.SPACES");
t!(test4: "mixed_up_snake_case with some _spaces" => "MIXED.UP.SNAKE.CASE.WITH.SOME.SPACES");
t!(test5: "kebab-case" => "KEBAB.CASE");
t!(test6: "SHOUTY_SNAKE_CASE" => "SHOUTY.SNAKE.CASE");
t!(test7: "snake_case" => "SNAKE.CASE");
t!(test8: "this-contains_ ALLKinds OfWord_Boundaries" => "THIS.CONTAINS.ALL.KINDS.OF.WORD.BOUNDARIES");
#[cfg(feature = "unicode")]
t!(test9: "XΣXΣ baffle" => "XΣXΣ.BAFFLE");
t!(test10: "XMLHttpRequest" => "XML.HTTP.REQUEST");
t!(test11: "SHOUTY.DOT.CASE" => "SHOUTY.DOT.CASE");
t!(test12: "dot.case" => "DOT.CASE");
t!(test13: "mixed.up. dot.case with some .spaces" => "MIXED.UP.DOT.CASE.WITH.SOME.SPACES");
}
3 changes: 3 additions & 0 deletions src/shouty_kebab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,7 @@ mod tests {
t!(test9: "XΣXΣ baffle" => "XΣXΣ-BAFFLE");
t!(test10: "XMLHttpRequest" => "XML-HTTP-REQUEST");
t!(test11: "SHOUTY-KEBAB-CASE" => "SHOUTY-KEBAB-CASE");
t!(test12: "dot.case" => "DOT-CASE");
t!(test13: "SHOUTY.DOT.CASE" => "SHOUTY-DOT-CASE");
t!(test14: "mixed.up. dot.case with some .spaces" => "MIXED-UP-DOT-CASE-WITH-SOME-SPACES");
}
3 changes: 3 additions & 0 deletions src/shouty_snake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,7 @@ mod tests {
t!(test8: "this-contains_ ALLKinds OfWord_Boundaries" => "THIS_CONTAINS_ALL_KINDS_OF_WORD_BOUNDARIES");
t!(test9: "XΣXΣ baffle" => "XΣXΣ_BAFFLE");
t!(test10: "XMLHttpRequest" => "XML_HTTP_REQUEST");
t!(test11: "dot.case" => "DOT_CASE");
t!(test12: "SHOUTY.DOT.CASE" => "SHOUTY_DOT_CASE");
t!(test13: "mixed.up. dot.case with some .spaces" => "MIXED_UP_DOT_CASE_WITH_SOME_SPACES");
}
3 changes: 3 additions & 0 deletions src/snake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,7 @@ mod tests {
t!(test23: "ABC123dEEf456FOO" => "abc123d_e_ef456_foo");
t!(test24: "abcDEF" => "abc_def");
t!(test25: "ABcDE" => "a_bc_de");
t!(test26: "dot.case" => "dot_case");
t!(test27: "SHOUTY.DOT.CASE" => "shouty_dot_case");
t!(test28: "mixed.up. dot.case with some .spaces" => "mixed_up_dot_case_with_some_spaces");
}
3 changes: 3 additions & 0 deletions src/title.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,7 @@ mod tests {
t!(test8: "this-contains_ ALLKinds OfWord_Boundaries" => "This Contains All Kinds Of Word Boundaries");
t!(test9: "XΣXΣ baffle" => "Xσxς Baffle");
t!(test10: "XMLHttpRequest" => "Xml Http Request");
t!(test11: "dot.case" => "Dot Case");
t!(test12: "SHOUTY.DOT.CASE" => "Shouty Dot Case");
t!(test13: "mixed.up. dot.case with some .spaces" => "Mixed Up Dot Case With Some Spaces");
}
3 changes: 3 additions & 0 deletions src/train.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,7 @@ mod tests {
t!(test23: "ABC123dEEf456FOO" => "Abc123d-E-Ef456-Foo");
t!(test24: "abcDEF" => "Abc-Def");
t!(test25: "ABcDE" => "A-Bc-De");
t!(test26: "dot.case" => "Dot-Case");
t!(test27: "SHOUTY.DOT.CASE" => "Shouty-Dot-Case");
t!(test28: "mixed.up. dot.case with some .spaces" => "Mixed-Up-Dot-Case-With-Some-Spaces");
}
3 changes: 3 additions & 0 deletions src/upper_camel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,7 @@ mod tests {
t!(test8: "this-contains_ ALLKinds OfWord_Boundaries" => "ThisContainsAllKindsOfWordBoundaries");
t!(test9: "XΣXΣ baffle" => "XσxςBaffle");
t!(test10: "XMLHttpRequest" => "XmlHttpRequest");
t!(test26: "dot.case" => "DotCase");
t!(test27: "SHOUTY.DOT.CASE" => "ShoutyDotCase");
t!(test28: "mixed.up. dot.case, with some .spaces" => "MixedUpDotCaseWithSomeSpaces");
}