Решение на Code Identifier от Николай Данаилов

Обратно към всички решения

Към профила на Николай Данаилов

Резултати

  • 19 точки от тестове
  • 0 бонус точки
  • 19 точки общо
  • 14 успешни тест(а)
  • 1 неуспешни тест(а)

Код

#[derive(Debug)]
pub struct CodeIdentifier {
pub words: Vec<String>
}
impl CodeIdentifier {
pub fn new(identifier: &str) -> Option<Self> {
let identifier: &str = identifier.trim();
if CodeIdentifier::is_valid(identifier) {
let code_identifier: CodeIdentifier = CodeIdentifier {
words: CodeIdentifier::parse_words(identifier)
};
Some(code_identifier)
} else {
None
}

match statement-а е мощен инструмент, но си заслужава да се използва, когато if-else не стига, или е по-тромав. В случая на булева стойност, по-четимо ще е, включително за хора, които не са супер запознати с езика, да си е if-клауза това.

}
pub fn camelcase(&self) -> String {
let mut res: String = "".to_string();
let mut iterator: std::slice::Iter<String> = self.words.iter();
// Add the first word to the result as is
let first_word: &str = iterator.next().unwrap();
res.push_str(first_word);
// Uppercase the first letter of all following words
for word in iterator {
res.push_str(&CodeIdentifier::titlecase_word(word));
}
res
}
pub fn titlecase(&self) -> String {
self.words.iter().map(|w| CodeIdentifier::titlecase_word(w))
.collect::<Vec<String>>().join("")
}
pub fn underscore(&self) -> String {
self.words.join("_")
}
pub fn kebabcase(&self) -> String {
self.words.join("-")
}
pub fn screaming_snakecase(&self) -> String {
self.underscore().to_uppercase()
}
fn parse_words(identifier: &str) -> Vec<String> {
identifier.split('_').map(|s| s.to_lowercase()).collect()
}
fn titlecase_word(word: &str) -> String {
let mut new_word: String = "".to_string();
let mut iterator: std::str::Chars = word.chars();
// Uppercase first letter
let first_letter: char = iterator.next().unwrap();
new_word.push(first_letter.to_uppercase().next().unwrap());
// Push into the result the rest of the chars as they are
new_word.push_str(&iterator.collect::<String>());
new_word
}
fn is_valid(identifier: &str) -> bool {
let mut iterator: std::str::Chars = identifier.chars();
let first_letter_option: Option<char> = iterator.next();
// If the string is empty, return false
// An empty string is invalid
if first_letter_option.is_none() {
return false;
}
let first_letter: char = first_letter_option.unwrap();
if !char::is_alphabetic(first_letter) {
return false;
}
for c in iterator {
if !char::is_alphabetic(c)
&& !char::is_numeric(c)
&& c != '_' {
return false;
}
}
true
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_words() {
let words = CodeIdentifier::parse_words("sOmE_VAR");
assert_eq!(words, vec!("some", "var"))
}
#[test]
fn titlecase_word() {
assert_eq!(CodeIdentifier::titlecase_word("some"), "Some");
}
#[test]
fn titlecase_word_cyrillic() {
assert_eq!(CodeIdentifier::titlecase_word("нещо"), "Нещо");
}
#[test]
fn is_valid() {
assert!(CodeIdentifier::is_valid("a"));
assert!(CodeIdentifier::is_valid("abc1"));
assert!(CodeIdentifier::is_valid("a123214_wq213"));
assert!(CodeIdentifier::is_valid("anr_"));
assert!(CodeIdentifier::is_valid("aqwewwqWErTW_weWeSvV_"));
assert!(!CodeIdentifier::is_valid(""));
assert!(!CodeIdentifier::is_valid("1abc"));
assert!(!CodeIdentifier::is_valid("_wqretewt"));
assert!(!CodeIdentifier::is_valid("qerew@erwr.com"));
assert!(!CodeIdentifier::is_valid("@@@"));
}
}

Лог от изпълнението

Compiling solution v0.1.0 (/tmp/d20190123-22631-168kzbp/solution)
    Finished dev [unoptimized + debuginfo] target(s) in 6.42s
     Running target/debug/deps/solution-2e785d603b538f71

running 4 tests
test tests::is_valid ... ok
test tests::parse_words ... ok
test tests::titlecase_word ... ok
test tests::titlecase_word_cyrillic ... ok

test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

     Running target/debug/deps/solution_test-29808948fb50ed3a

running 15 tests
test solution_test::test_both_static_and_dynamic_strings ... ok
test solution_test::test_camelcase_basic ... ok
test solution_test::test_cyrillic1 ... ok
test solution_test::test_digits1 ... ok
test solution_test::test_digits2 ... ok
test solution_test::test_digits3 ... ok
test solution_test::test_kebabcase_basic ... ok
test solution_test::test_multibyte_uppercase ... FAILED
test solution_test::test_normalize_case1 ... ok
test solution_test::test_normalize_case2 ... ok
test solution_test::test_screaming_snakecase_basic ... ok
test solution_test::test_titlecase_basic ... ok
test solution_test::test_underscore_basic ... ok
test solution_test::test_validity ... ok
test solution_test::test_whitespace ... ok

failures:

---- solution_test::test_multibyte_uppercase stdout ----
thread 'solution_test::test_multibyte_uppercase' panicked at 'assertion failed: `(left == right)`
  left: `"someSpecialCase"`,
 right: `"someSSpecialCase"`', tests/solution_test.rs:104:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.


failures:
    solution_test::test_multibyte_uppercase

test result: FAILED. 14 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '--test solution_test'

История (9 версии и 8 коментара)

Николай качи първо решение на 20.10.2018 18:28 (преди почти 7 години)

Николай качи решение на 20.10.2018 18:53 (преди почти 7 години)

#[derive(Debug)]
pub struct CodeIdentifier {
pub words: Vec<String>
}
impl CodeIdentifier {
pub fn new(identifier: &str) -> Option<Self> {
match CodeIdentifier::is_valid(identifier) {
true => {
let code_identifier = CodeIdentifier {
words: CodeIdentifier::parse_words(identifier)
};
Some(code_identifier)
},
false => None
}
}
pub fn camelcase(&self) -> String {
let mut res: String = "".to_string();
let mut iterator: std::slice::Iter<String> = self.words.iter();
// Add the first word to the result as is
let first_word: &String = iterator.next().unwrap();
res.push_str(&first_word);
// Uppercase the first letter of all following words
for word in iterator {
res.push_str(&CodeIdentifier::titlecase_word(word));
}
res
}
pub fn titlecase(&self) -> String {
self.words.iter().map( |w| CodeIdentifier::titlecase_word(w))
.collect::<Vec<String>>().join("")
}
pub fn underscore(&self) -> String {
self.words.join("_")
}
pub fn kebabcase(&self) -> String {
self.words.join("-")
}
pub fn screaming_snakecase(&self) -> String {
self.underscore().to_uppercase()
}
fn split_identifier(identifier: &String) -> Vec<String> {
let str_words: Vec<&str> = identifier.split('_').collect();
// Get ownership of the slices, by making them into
// new strings. I am converting them
// through mapping, beacause I didn't find a better
// way to split into String elements.
str_words.iter().map(|&s| String::from(s)).collect()
}
fn parse_words(identifier: &str) -> Vec<String> {
let identifier_string: String = String::from(identifier);
let split_words: Vec<String> = CodeIdentifier::split_identifier(&identifier_string);
split_words.iter().map(|s| s.to_lowercase()).collect()
}
fn titlecase_word(word: &String) -> String {
let mut new_word: String = String::from("");
let mut iterator: std::str::Chars = word.chars();
let first_letter: char = iterator.next().unwrap();
new_word.push(first_letter.to_uppercase().next().unwrap());
new_word.push_str(&iterator.collect::<String>());
new_word
}
fn is_valid(identifier: &str) -> bool {
- let first_letter: char = identifier.chars().next().unwrap();
+ let first_letter_option: Option<char> = identifier.chars().next();
- if(!char::is_alphabetic(first_letter)) {
+ // If the string is empty, return false
+ // An empty string is invalid
+ if first_letter_option.is_none() {
+ return false;
+ }
+
+ let first_letter: char = first_letter_option.unwrap();
+
+ if !char::is_alphabetic(first_letter) {
return false;
}
for c in identifier.chars() {
if !char::is_alphabetic(c)
&& !char::is_numeric(c)
&& c != '_' {
return false;
}
}
true
}
}

Николай качи решение на 20.10.2018 18:55 (преди почти 7 години)

#[derive(Debug)]
pub struct CodeIdentifier {
pub words: Vec<String>
}
impl CodeIdentifier {
pub fn new(identifier: &str) -> Option<Self> {
match CodeIdentifier::is_valid(identifier) {
true => {
let code_identifier = CodeIdentifier {
words: CodeIdentifier::parse_words(identifier)
};
Some(code_identifier)
},
false => None
}
}
pub fn camelcase(&self) -> String {
let mut res: String = "".to_string();
let mut iterator: std::slice::Iter<String> = self.words.iter();
// Add the first word to the result as is
let first_word: &String = iterator.next().unwrap();
res.push_str(&first_word);
// Uppercase the first letter of all following words
for word in iterator {
res.push_str(&CodeIdentifier::titlecase_word(word));
}
res
}
pub fn titlecase(&self) -> String {
self.words.iter().map( |w| CodeIdentifier::titlecase_word(w))
.collect::<Vec<String>>().join("")
}
pub fn underscore(&self) -> String {
self.words.join("_")
}
pub fn kebabcase(&self) -> String {
self.words.join("-")
}
pub fn screaming_snakecase(&self) -> String {
self.underscore().to_uppercase()
}
fn split_identifier(identifier: &String) -> Vec<String> {
let str_words: Vec<&str> = identifier.split('_').collect();
// Get ownership of the slices, by making them into
// new strings. I am converting them
// through mapping, beacause I didn't find a better
// way to split into String elements.
str_words.iter().map(|&s| String::from(s)).collect()
}
fn parse_words(identifier: &str) -> Vec<String> {
let identifier_string: String = String::from(identifier);
let split_words: Vec<String> = CodeIdentifier::split_identifier(&identifier_string);
split_words.iter().map(|s| s.to_lowercase()).collect()
}
fn titlecase_word(word: &String) -> String {
let mut new_word: String = String::from("");
let mut iterator: std::str::Chars = word.chars();
let first_letter: char = iterator.next().unwrap();
new_word.push(first_letter.to_uppercase().next().unwrap());
new_word.push_str(&iterator.collect::<String>());
new_word
}
fn is_valid(identifier: &str) -> bool {
- let first_letter_option: Option<char> = identifier.chars().next();
+ let mut iterator = identifier.chars();
+ let first_letter_option: Option<char> = iterator.next();
// If the string is empty, return false
// An empty string is invalid
if first_letter_option.is_none() {
return false;
}
let first_letter: char = first_letter_option.unwrap();
if !char::is_alphabetic(first_letter) {
return false;
}
- for c in identifier.chars() {
+ for c in iterator {
if !char::is_alphabetic(c)
&& !char::is_numeric(c)
&& c != '_' {
return false;
}
}
true
}
}

Николай качи решение на 21.10.2018 09:11 (преди почти 7 години)

#[derive(Debug)]
pub struct CodeIdentifier {
pub words: Vec<String>
}
impl CodeIdentifier {
pub fn new(identifier: &str) -> Option<Self> {
+ let identifier = identifier.trim();
+
match CodeIdentifier::is_valid(identifier) {
true => {
let code_identifier = CodeIdentifier {
words: CodeIdentifier::parse_words(identifier)
};
Some(code_identifier)
},
false => None
}
}
pub fn camelcase(&self) -> String {
let mut res: String = "".to_string();
let mut iterator: std::slice::Iter<String> = self.words.iter();
// Add the first word to the result as is
let first_word: &String = iterator.next().unwrap();
res.push_str(&first_word);
// Uppercase the first letter of all following words
for word in iterator {
res.push_str(&CodeIdentifier::titlecase_word(word));
}
res
}
pub fn titlecase(&self) -> String {
self.words.iter().map( |w| CodeIdentifier::titlecase_word(w))
.collect::<Vec<String>>().join("")
}
pub fn underscore(&self) -> String {
self.words.join("_")
}
pub fn kebabcase(&self) -> String {
self.words.join("-")
}
pub fn screaming_snakecase(&self) -> String {
self.underscore().to_uppercase()
}
fn split_identifier(identifier: &String) -> Vec<String> {
let str_words: Vec<&str> = identifier.split('_').collect();
// Get ownership of the slices, by making them into
// new strings. I am converting them
// through mapping, beacause I didn't find a better
// way to split into String elements.
str_words.iter().map(|&s| String::from(s)).collect()
}
fn parse_words(identifier: &str) -> Vec<String> {
let identifier_string: String = String::from(identifier);
let split_words: Vec<String> = CodeIdentifier::split_identifier(&identifier_string);
split_words.iter().map(|s| s.to_lowercase()).collect()
}
fn titlecase_word(word: &String) -> String {
let mut new_word: String = String::from("");
let mut iterator: std::str::Chars = word.chars();
let first_letter: char = iterator.next().unwrap();
new_word.push(first_letter.to_uppercase().next().unwrap());
new_word.push_str(&iterator.collect::<String>());
new_word
}
fn is_valid(identifier: &str) -> bool {
let mut iterator = identifier.chars();
let first_letter_option: Option<char> = iterator.next();
// If the string is empty, return false
// An empty string is invalid
if first_letter_option.is_none() {
return false;
}
let first_letter: char = first_letter_option.unwrap();
if !char::is_alphabetic(first_letter) {
return false;
}
for c in iterator {
if !char::is_alphabetic(c)
&& !char::is_numeric(c)
&& c != '_' {
return false;
}
}
true
}
}

Николай качи решение на 21.10.2018 09:38 (преди почти 7 години)

#[derive(Debug)]
pub struct CodeIdentifier {
pub words: Vec<String>
}
impl CodeIdentifier {
pub fn new(identifier: &str) -> Option<Self> {
- let identifier = identifier.trim();
+ let identifier: &str = identifier.trim();
match CodeIdentifier::is_valid(identifier) {
true => {
- let code_identifier = CodeIdentifier {
+ let code_identifier: CodeIdentifier = CodeIdentifier {
words: CodeIdentifier::parse_words(identifier)
};
Some(code_identifier)
},
false => None
}
}
pub fn camelcase(&self) -> String {
let mut res: String = "".to_string();
let mut iterator: std::slice::Iter<String> = self.words.iter();
// Add the first word to the result as is
- let first_word: &String = iterator.next().unwrap();
- res.push_str(&first_word);
+ let first_word: &str = iterator.next().unwrap();
+ res.push_str(first_word);
// Uppercase the first letter of all following words
for word in iterator {
res.push_str(&CodeIdentifier::titlecase_word(word));
}
res
}
pub fn titlecase(&self) -> String {
- self.words.iter().map( |w| CodeIdentifier::titlecase_word(w))
+ self.words.iter().map(|w| CodeIdentifier::titlecase_word(w))
.collect::<Vec<String>>().join("")
}
pub fn underscore(&self) -> String {
self.words.join("_")
}
pub fn kebabcase(&self) -> String {
self.words.join("-")
}
pub fn screaming_snakecase(&self) -> String {
self.underscore().to_uppercase()
}
- fn split_identifier(identifier: &String) -> Vec<String> {
+ fn split_identifier(identifier: &str) -> Vec<String> {
let str_words: Vec<&str> = identifier.split('_').collect();
// Get ownership of the slices, by making them into
// new strings. I am converting them
// through mapping, beacause I didn't find a better
// way to split into String elements.
- str_words.iter().map(|&s| String::from(s)).collect()
+ str_words.iter().map(|&s| s.to_string()).collect()
}
fn parse_words(identifier: &str) -> Vec<String> {
- let identifier_string: String = String::from(identifier);
- let split_words: Vec<String> = CodeIdentifier::split_identifier(&identifier_string);
- split_words.iter().map(|s| s.to_lowercase()).collect()
+ CodeIdentifier::split_identifier(identifier).iter()
+ .map(|s| s.to_lowercase()).collect()
}
- fn titlecase_word(word: &String) -> String {
- let mut new_word: String = String::from("");
+ fn titlecase_word(word: &str) -> String {
+ let mut new_word: String = "".to_string();
let mut iterator: std::str::Chars = word.chars();
+ // Uppercase first letter
let first_letter: char = iterator.next().unwrap();
new_word.push(first_letter.to_uppercase().next().unwrap());
+
+ // Push into the result the rest of the chars as they are
new_word.push_str(&iterator.collect::<String>());
+
new_word
}
fn is_valid(identifier: &str) -> bool {
- let mut iterator = identifier.chars();
+ let mut iterator: std::str::Chars = identifier.chars();
let first_letter_option: Option<char> = iterator.next();
// If the string is empty, return false
// An empty string is invalid
if first_letter_option.is_none() {
return false;
}
let first_letter: char = first_letter_option.unwrap();
if !char::is_alphabetic(first_letter) {
return false;
}
for c in iterator {
if !char::is_alphabetic(c)
&& !char::is_numeric(c)
&& c != '_' {
return false;
}
}
true
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn split_identifier() {
+ let split = CodeIdentifier::split_identifier("some_var");
+ assert_eq!(split, vec!("some", "var"));
+ }
+
+ #[test]
+ fn parse_words() {
+ let words = CodeIdentifier::parse_words("sOmE_VAR");
+ assert_eq!(words, vec!("some", "var"))
+ }
+
+ #[test]
+ fn titlecase_word() {
+ assert_eq!(CodeIdentifier::titlecase_word("some"), "Some");
+ }
+
+ #[test]
+ fn titlecase_word_cyrillic() {
+ assert_eq!(CodeIdentifier::titlecase_word("нещо"), "Нещо");
+ }
+
+ #[test]
+ fn returns_some_when_valid() {
+ assert!(CodeIdentifier::is_valid("a"));
+ assert!(CodeIdentifier::is_valid("a123214_wq213"));
+ assert!(CodeIdentifier::is_valid("anr_"));
+ assert!(CodeIdentifier::is_valid("aqwewwqWErTW_weWeSvV_"));
}
}

Николай качи решение на 21.10.2018 09:39 (преди почти 7 години)

#[derive(Debug)]
pub struct CodeIdentifier {
pub words: Vec<String>
}
impl CodeIdentifier {
pub fn new(identifier: &str) -> Option<Self> {
let identifier: &str = identifier.trim();
match CodeIdentifier::is_valid(identifier) {
true => {
let code_identifier: CodeIdentifier = CodeIdentifier {
words: CodeIdentifier::parse_words(identifier)
};
Some(code_identifier)
},
false => None
}
}
pub fn camelcase(&self) -> String {
let mut res: String = "".to_string();
let mut iterator: std::slice::Iter<String> = self.words.iter();
// Add the first word to the result as is
let first_word: &str = iterator.next().unwrap();
res.push_str(first_word);
// Uppercase the first letter of all following words
for word in iterator {
res.push_str(&CodeIdentifier::titlecase_word(word));
}
res
}
pub fn titlecase(&self) -> String {
self.words.iter().map(|w| CodeIdentifier::titlecase_word(w))
.collect::<Vec<String>>().join("")
}
pub fn underscore(&self) -> String {
self.words.join("_")
}
pub fn kebabcase(&self) -> String {
self.words.join("-")
}
pub fn screaming_snakecase(&self) -> String {
self.underscore().to_uppercase()
}
fn split_identifier(identifier: &str) -> Vec<String> {
let str_words: Vec<&str> = identifier.split('_').collect();
// Get ownership of the slices, by making them into
// new strings. I am converting them
// through mapping, beacause I didn't find a better
// way to split into String elements.
str_words.iter().map(|&s| s.to_string()).collect()
}
fn parse_words(identifier: &str) -> Vec<String> {
CodeIdentifier::split_identifier(identifier).iter()
.map(|s| s.to_lowercase()).collect()
}
fn titlecase_word(word: &str) -> String {
let mut new_word: String = "".to_string();
let mut iterator: std::str::Chars = word.chars();
// Uppercase first letter
let first_letter: char = iterator.next().unwrap();
new_word.push(first_letter.to_uppercase().next().unwrap());
// Push into the result the rest of the chars as they are
new_word.push_str(&iterator.collect::<String>());
new_word
}
fn is_valid(identifier: &str) -> bool {
let mut iterator: std::str::Chars = identifier.chars();
let first_letter_option: Option<char> = iterator.next();
// If the string is empty, return false
// An empty string is invalid
if first_letter_option.is_none() {
return false;
}
let first_letter: char = first_letter_option.unwrap();
if !char::is_alphabetic(first_letter) {
return false;
}
for c in iterator {
if !char::is_alphabetic(c)
&& !char::is_numeric(c)
&& c != '_' {
return false;
}
}
true
}
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn split_identifier() {
- let split = CodeIdentifier::split_identifier("some_var");
- assert_eq!(split, vec!("some", "var"));
- }
-
- #[test]
- fn parse_words() {
- let words = CodeIdentifier::parse_words("sOmE_VAR");
- assert_eq!(words, vec!("some", "var"))
- }
-
- #[test]
- fn titlecase_word() {
- assert_eq!(CodeIdentifier::titlecase_word("some"), "Some");
- }
-
- #[test]
- fn titlecase_word_cyrillic() {
- assert_eq!(CodeIdentifier::titlecase_word("нещо"), "Нещо");
- }
-
- #[test]
- fn returns_some_when_valid() {
- assert!(CodeIdentifier::is_valid("a"));
- assert!(CodeIdentifier::is_valid("a123214_wq213"));
- assert!(CodeIdentifier::is_valid("anr_"));
- assert!(CodeIdentifier::is_valid("aqwewwqWErTW_weWeSvV_"));
- }
-}

Николай качи решение на 21.10.2018 09:41 (преди почти 7 години)

#[derive(Debug)]
pub struct CodeIdentifier {
pub words: Vec<String>
}
impl CodeIdentifier {
pub fn new(identifier: &str) -> Option<Self> {
let identifier: &str = identifier.trim();
match CodeIdentifier::is_valid(identifier) {
true => {
let code_identifier: CodeIdentifier = CodeIdentifier {
words: CodeIdentifier::parse_words(identifier)
};
Some(code_identifier)
},
false => None
}
}
pub fn camelcase(&self) -> String {
let mut res: String = "".to_string();
let mut iterator: std::slice::Iter<String> = self.words.iter();
// Add the first word to the result as is
let first_word: &str = iterator.next().unwrap();
res.push_str(first_word);
// Uppercase the first letter of all following words
for word in iterator {
res.push_str(&CodeIdentifier::titlecase_word(word));
}
res
}
pub fn titlecase(&self) -> String {
self.words.iter().map(|w| CodeIdentifier::titlecase_word(w))
.collect::<Vec<String>>().join("")
}
pub fn underscore(&self) -> String {
self.words.join("_")
}
pub fn kebabcase(&self) -> String {
self.words.join("-")
}
pub fn screaming_snakecase(&self) -> String {
self.underscore().to_uppercase()
}
fn split_identifier(identifier: &str) -> Vec<String> {
let str_words: Vec<&str> = identifier.split('_').collect();
// Get ownership of the slices, by making them into
// new strings. I am converting them
// through mapping, beacause I didn't find a better
// way to split into String elements.
str_words.iter().map(|&s| s.to_string()).collect()
}
fn parse_words(identifier: &str) -> Vec<String> {
CodeIdentifier::split_identifier(identifier).iter()
.map(|s| s.to_lowercase()).collect()
}
fn titlecase_word(word: &str) -> String {
let mut new_word: String = "".to_string();
let mut iterator: std::str::Chars = word.chars();
// Uppercase first letter
let first_letter: char = iterator.next().unwrap();
new_word.push(first_letter.to_uppercase().next().unwrap());
// Push into the result the rest of the chars as they are
new_word.push_str(&iterator.collect::<String>());
new_word
}
fn is_valid(identifier: &str) -> bool {
let mut iterator: std::str::Chars = identifier.chars();
let first_letter_option: Option<char> = iterator.next();
// If the string is empty, return false
// An empty string is invalid
if first_letter_option.is_none() {
return false;
}
let first_letter: char = first_letter_option.unwrap();
if !char::is_alphabetic(first_letter) {
return false;
}
for c in iterator {
if !char::is_alphabetic(c)
&& !char::is_numeric(c)
&& c != '_' {
return false;
}
}
true
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn split_identifier() {
+ let split = CodeIdentifier::split_identifier("some_var");
+ assert_eq!(split, vec!("some", "var"));
+ }
+
+ #[test]
+ fn parse_words() {
+ let words = CodeIdentifier::parse_words("sOmE_VAR");
+ assert_eq!(words, vec!("some", "var"))
+ }
+
+ #[test]
+ fn titlecase_word() {
+ assert_eq!(CodeIdentifier::titlecase_word("some"), "Some");
+ }
+
+ #[test]
+ fn titlecase_word_cyrillic() {
+ assert_eq!(CodeIdentifier::titlecase_word("нещо"), "Нещо");
+ }
+
+ #[test]
+ fn returns_some_when_valid() {
+ assert!(CodeIdentifier::is_valid("a"));
+ assert!(CodeIdentifier::is_valid("a123214_wq213"));
+ assert!(CodeIdentifier::is_valid("anr_"));
+ assert!(CodeIdentifier::is_valid("aqwewwqWErTW_weWeSvV_"));
+ }
+}

Николай качи решение на 21.10.2018 09:44 (преди почти 7 години)

#[derive(Debug)]
pub struct CodeIdentifier {
pub words: Vec<String>
}
impl CodeIdentifier {
pub fn new(identifier: &str) -> Option<Self> {
let identifier: &str = identifier.trim();
match CodeIdentifier::is_valid(identifier) {
true => {
let code_identifier: CodeIdentifier = CodeIdentifier {
words: CodeIdentifier::parse_words(identifier)
};
Some(code_identifier)
},
false => None
}

match statement-а е мощен инструмент, но си заслужава да се използва, когато if-else не стига, или е по-тромав. В случая на булева стойност, по-четимо ще е, включително за хора, които не са супер запознати с езика, да си е if-клауза това.

}
pub fn camelcase(&self) -> String {
let mut res: String = "".to_string();
let mut iterator: std::slice::Iter<String> = self.words.iter();
// Add the first word to the result as is
let first_word: &str = iterator.next().unwrap();
res.push_str(first_word);
// Uppercase the first letter of all following words
for word in iterator {
res.push_str(&CodeIdentifier::titlecase_word(word));
}
res
}
pub fn titlecase(&self) -> String {
self.words.iter().map(|w| CodeIdentifier::titlecase_word(w))
.collect::<Vec<String>>().join("")
}
pub fn underscore(&self) -> String {
self.words.join("_")
}
pub fn kebabcase(&self) -> String {
self.words.join("-")
}
pub fn screaming_snakecase(&self) -> String {
self.underscore().to_uppercase()
}
fn split_identifier(identifier: &str) -> Vec<String> {
let str_words: Vec<&str> = identifier.split('_').collect();
// Get ownership of the slices, by making them into
// new strings. I am converting them
// through mapping, beacause I didn't find a better
// way to split into String elements.
str_words.iter().map(|&s| s.to_string()).collect()

Това е смислен начин, да. split ще ти върне итератор към &str, понеже, в зависимост от това какво правиш, може да ти е достатъчно. В твоя случай... Е, би могло да ти бъде достатъчно при малко по-различна структура, но е ок да алокираш низове. Ще говорим скоро за lifetimes и за боравене с псевдоними вместо с owned типове.

Можеш да опростиш малко кода като избегнеш първия collect: identifier.split('_').map(|s| s.to_string()).collect().

Успях доста да опростя кода тук, особено като осъзнах и че to_lowercase директно ми връща String. Беше ми малко объркващо кога точно се използва &str и кога String, но май придобих интуиция донякъде. Малко се обърках, но казваш, че може да се мине без пазене на String и съответно без взимане на ownership? Идеята ми беше, че отвън ми се подава &str, но искам после да имам думите като String, защото този външния &str не се знае до кога ще живее и т.н. Даже не знам дали е възможно да се мине без да пазя String въобще, а да ги пазя като &str. Не съм пробвал. Така си го представям, пък не съм сигурен дали е съвсем вярно.

Може да се пази &str в случая, но трябва да се ползват lifetimes, което ще е ужасно объркващо. Още повече, че в тази конкретна задача, аз бих обработил низа преди да го запазя, което все пак би изисквало ownership, но това е друга тема.

Засега е напълно ок да държиш String-ове. Ще говорим още доста за приемането и връщането на reference types скоро (може би не идващата седмица, а тази след нея).

}
fn parse_words(identifier: &str) -> Vec<String> {
CodeIdentifier::split_identifier(identifier).iter()
.map(|s| s.to_lowercase()).collect()
}
fn titlecase_word(word: &str) -> String {
let mut new_word: String = "".to_string();
let mut iterator: std::str::Chars = word.chars();
// Uppercase first letter
let first_letter: char = iterator.next().unwrap();
new_word.push(first_letter.to_uppercase().next().unwrap());
// Push into the result the rest of the chars as they are
new_word.push_str(&iterator.collect::<String>());
new_word
}
fn is_valid(identifier: &str) -> bool {
let mut iterator: std::str::Chars = identifier.chars();
let first_letter_option: Option<char> = iterator.next();
// If the string is empty, return false
// An empty string is invalid
if first_letter_option.is_none() {
return false;
}
let first_letter: char = first_letter_option.unwrap();
if !char::is_alphabetic(first_letter) {
return false;
}
for c in iterator {
if !char::is_alphabetic(c)
&& !char::is_numeric(c)
&& c != '_' {
return false;
}
}
true
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn split_identifier() {
let split = CodeIdentifier::split_identifier("some_var");
assert_eq!(split, vec!("some", "var"));
}
#[test]
fn parse_words() {
let words = CodeIdentifier::parse_words("sOmE_VAR");
assert_eq!(words, vec!("some", "var"))
}
#[test]
fn titlecase_word() {
assert_eq!(CodeIdentifier::titlecase_word("some"), "Some");
}
#[test]
fn titlecase_word_cyrillic() {
assert_eq!(CodeIdentifier::titlecase_word("нещо"), "Нещо");
}
#[test]
- fn returns_some_when_valid() {
+ fn is_valid() {
assert!(CodeIdentifier::is_valid("a"));
assert!(CodeIdentifier::is_valid("a123214_wq213"));
assert!(CodeIdentifier::is_valid("anr_"));
assert!(CodeIdentifier::is_valid("aqwewwqWErTW_weWeSvV_"));
+
+ assert!(!CodeIdentifier::is_valid(""));
+ assert!(!CodeIdentifier::is_valid("1ewer"));
+ assert!(!CodeIdentifier::is_valid("_wqretewt"));
+ assert!(!CodeIdentifier::is_valid("qerew@erwr.com"));
}
}

Добра работа, харесва ми и че си си имплементирал собствени тестове, винаги е чудесна идея да си валидираш assumption-ите.

Едно генерално нещо, което бих те посъветвал, е да не слагаш експлицитен тип на всичко. В някои отношения е по-експлицитно, съответно по-ясно, но в други те ограничава -- може би rust би успял да ти алокира Vec<&str>, но тъй като си поискал Vec<String>, кода няма да се компилира. А и самия код става доста verbose.

Другия проблем е, че в някои ситуации, самия тип може да стане доста сложен, особено като стигнем до анонимни функции. Пробвай да провериш какъв е типа на .map(|s| s.to_string()), например :).

Конвенцията в Rust е да изпускаме type annotations, освен ако не са задължителни. Това има хубавото качество, че ако видиш експлицитен type annotation, това комуникира с теб, че е бил задължителен по една или друга причина.

Даже имам и още десетина integration теста, но качих само този файл. :D

Усетих се, че май става малко прекалено експлицитното указване на типове, но исках като за първо домашно да го направя така - да придобия представа кои неща какво връщат точно и т.н.

.map видях, че връща някакъв итератор std::iter::Map. Обаче не ми стана много ясно защо този итератор е по-специален, а не е просто std::iter::Iterator или там какъвто е нормалния итератор.

Николай качи решение на 21.10.2018 16:47 (преди почти 7 години)

#[derive(Debug)]
pub struct CodeIdentifier {
pub words: Vec<String>
}
impl CodeIdentifier {
pub fn new(identifier: &str) -> Option<Self> {
let identifier: &str = identifier.trim();
- match CodeIdentifier::is_valid(identifier) {
- true => {
- let code_identifier: CodeIdentifier = CodeIdentifier {
- words: CodeIdentifier::parse_words(identifier)
- };
+ if CodeIdentifier::is_valid(identifier) {
+ let code_identifier: CodeIdentifier = CodeIdentifier {
+ words: CodeIdentifier::parse_words(identifier)
+ };
- Some(code_identifier)
- },
- false => None
+ Some(code_identifier)
+ } else {
+ None
}
}
pub fn camelcase(&self) -> String {
let mut res: String = "".to_string();
let mut iterator: std::slice::Iter<String> = self.words.iter();
// Add the first word to the result as is
let first_word: &str = iterator.next().unwrap();
res.push_str(first_word);
// Uppercase the first letter of all following words
for word in iterator {
res.push_str(&CodeIdentifier::titlecase_word(word));
}
res
}
pub fn titlecase(&self) -> String {
self.words.iter().map(|w| CodeIdentifier::titlecase_word(w))
.collect::<Vec<String>>().join("")
}
pub fn underscore(&self) -> String {
self.words.join("_")
}
pub fn kebabcase(&self) -> String {
self.words.join("-")
}
pub fn screaming_snakecase(&self) -> String {
self.underscore().to_uppercase()
}
- fn split_identifier(identifier: &str) -> Vec<String> {
- let str_words: Vec<&str> = identifier.split('_').collect();
-
- // Get ownership of the slices, by making them into
- // new strings. I am converting them
- // through mapping, beacause I didn't find a better
- // way to split into String elements.
- str_words.iter().map(|&s| s.to_string()).collect()
- }
-
fn parse_words(identifier: &str) -> Vec<String> {
- CodeIdentifier::split_identifier(identifier).iter()
- .map(|s| s.to_lowercase()).collect()
+ identifier.split('_').map(|s| s.to_lowercase()).collect()
}
fn titlecase_word(word: &str) -> String {
let mut new_word: String = "".to_string();
let mut iterator: std::str::Chars = word.chars();
// Uppercase first letter
let first_letter: char = iterator.next().unwrap();
new_word.push(first_letter.to_uppercase().next().unwrap());
// Push into the result the rest of the chars as they are
new_word.push_str(&iterator.collect::<String>());
new_word
}
fn is_valid(identifier: &str) -> bool {
let mut iterator: std::str::Chars = identifier.chars();
let first_letter_option: Option<char> = iterator.next();
// If the string is empty, return false
// An empty string is invalid
if first_letter_option.is_none() {
return false;
}
let first_letter: char = first_letter_option.unwrap();
if !char::is_alphabetic(first_letter) {
return false;
}
for c in iterator {
if !char::is_alphabetic(c)
&& !char::is_numeric(c)
&& c != '_' {
return false;
}
}
true
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
- fn split_identifier() {
- let split = CodeIdentifier::split_identifier("some_var");
- assert_eq!(split, vec!("some", "var"));
- }
-
- #[test]
fn parse_words() {
let words = CodeIdentifier::parse_words("sOmE_VAR");
assert_eq!(words, vec!("some", "var"))
}
#[test]
fn titlecase_word() {
assert_eq!(CodeIdentifier::titlecase_word("some"), "Some");
}
#[test]
fn titlecase_word_cyrillic() {
assert_eq!(CodeIdentifier::titlecase_word("нещо"), "Нещо");
}
#[test]
fn is_valid() {
assert!(CodeIdentifier::is_valid("a"));
+ assert!(CodeIdentifier::is_valid("abc1"));
assert!(CodeIdentifier::is_valid("a123214_wq213"));
assert!(CodeIdentifier::is_valid("anr_"));
assert!(CodeIdentifier::is_valid("aqwewwqWErTW_weWeSvV_"));
assert!(!CodeIdentifier::is_valid(""));
- assert!(!CodeIdentifier::is_valid("1ewer"));
+ assert!(!CodeIdentifier::is_valid("1abc"));
assert!(!CodeIdentifier::is_valid("_wqretewt"));
assert!(!CodeIdentifier::is_valid("qerew@erwr.com"));
+ assert!(!CodeIdentifier::is_valid("@@@"));
}
}

Тцъ, не връща просто std::iter::Map, връща (примерно) std::iter::Map<std::slice::Iter<'_, std::string::String>, [closure@src/lib.rs:90:46: 90:63]>. (Тоест, Map<тип-на-итератора-до-момента, тип-на-функцията>.) Типа [closure@src/lib.rs:90:46: 90:63] е конкретния тип на конкретната анонимна функция, дефинирана на конкретния ред/колона в сорса :). Това буквално няма как да го напишеш на ръка.

Има си начин да се работи с нещо такова, казва се impl Trait, и вероятно скоро ще говорим за него.

За другото - да, няма никакъв проблем да експериментираш, особено в домашните. Препоръчвам го даже, стига решението да минава :).