Решение на Code Identifier от Димитър Николов

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

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

Резултати

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

Код

// first_letter Vector of pairs
// \ /
// Pattern Idea: SoMe_vAr ---> (S) + [("ome", Some('v')) + ("ar", None)]
// / \
// unchanged part of name symbol connecting parts of the name
use std::str::Chars;
#[derive(Debug)]
struct Node {
pub unchanged_text: String,
pub connecting_char: Option<char>,
}
#[derive(Debug)]
pub struct CodeIdentifier {
first_letter: char,
text_structure: Vec<Node>,
}
impl CodeIdentifier {
pub fn new(variable_name: &str) -> Option<Self> {
let working_string = Self::trim_white_chars(variable_name);
if Self::check_valid(&working_string) {
let code_identifier = CodeIdentifier {
first_letter: Self::get_first_letter(&working_string),
text_structure: Self::process_text(&working_string),
};
Some(code_identifier)
}
else {
None
}
}
pub fn camelcase(&self) -> String {
let mut result = self.first_letter.to_lowercase().to_string();
for node in &self.text_structure {
result.push_str(&node.unchanged_text);
result.push_str(&Self::camelcase_connector(&node.connecting_char));
}
result
}
pub fn titlecase(&self) -> String {
let mut result = self.first_letter.to_uppercase().to_string();
for node in &self.text_structure {
result.push_str(&node.unchanged_text);
result.push_str(&Self::camelcase_connector(&node.connecting_char));
}
result
}
pub fn kebabcase(&self) -> String {
let mut result = self.first_letter.to_lowercase().to_string();
for node in &self.text_structure {
result.push_str(&node.unchanged_text);
result.push_str(&Self::kebabcase_connector(&node.connecting_char));
}
result
}
pub fn underscore(&self) -> String {
let mut result = self.first_letter.to_lowercase().to_string();
for node in &self.text_structure {
result.push_str(&node.unchanged_text);
result.push_str(&Self::underscore_connector(&node.connecting_char));
}
result
}
pub fn screaming_snakecase(&self) -> String {
let mut result = self.first_letter.to_uppercase().to_string();
for node in &self.text_structure {
result.push_str(&node.unchanged_text.to_uppercase());
result.push_str(&Self::screaming_snakecase_connector(&node.connecting_char));
}
result
}
fn check_valid(working_string: &String) -> bool {
let mut string_iterator = working_string.chars();
let mut result: bool;
match string_iterator.next() {
Some(c) => result = c.is_alphabetic(),
None => result = false,
}
if result {
loop {
match string_iterator.next() {
Some(c) => {
if Self::is_not_valid_character(c) {
result = false
}
}
None => break,
}
}
}
result
}
fn process_text(working_string: &String) -> Vec<Node> {
let mut result = Vec::new();
let normalized_string = working_string.to_lowercase();
let mut string_iterator = normalized_string.chars();
//Skip first letter
string_iterator.next();
let mut current_node = Node {
unchanged_text: String::new(),
connecting_char: None,
};
let mut block_start_pos: usize = 1;
let mut block_size: usize = 0;
loop {
match string_iterator.next() {
Some(c) => {

Тук вместо да правиш безкраен цикъл, който да break-неш при None, вероятно можеше да направиш for c in string_iterator. Или поне не виждам причина да не се получи.

Същото се отнася и за по-долу.

if c.is_lowercase() || c.is_numeric() {
block_size += 1;
continue;
}
else if Self::is_separator(c) {
current_node.unchanged_text = normalized_string.chars().skip(block_start_pos).take(block_size).collect::<String>();
block_start_pos += block_size;
current_node.connecting_char = Self::skip_connecting(&mut string_iterator, &mut block_start_pos);
result.push(current_node);
current_node = Node {
unchanged_text: String::new(),
connecting_char: None,
};
block_size = 0;
}
},
None => break,
}
}
if block_size != 0 {
current_node.unchanged_text = normalized_string.chars().skip(block_start_pos).take(block_size).collect::<String>();
result.push(current_node);
}
result
}
fn is_separator(character: char) -> bool {
if character == '_' || character == '-' {
true
}
else {
false
}
}
fn skip_connecting(iterator: &mut Chars, position: &mut usize) -> Option<char> {
let mut result = None;
loop {
match iterator.next() {
Some(c) => {
*position += 1;
if false == Self::is_separator(c) {
result = Some(c);
*position += 1;
break;
}
}
None => break,
}
}
result
}
fn is_not_valid_character(character: char) -> bool {
if character.is_alphabetic() || character.is_numeric() || character == '_' || character == '-' {
false
}
else {
true
}
}
fn get_first_letter(working_string: &String) -> char {
working_string.chars().next().unwrap()
}

Това би гръмнало за празен низ. Добра идея е да не разчиташ, че next() винаги ще ти даде Some. Може би ако си проверил някъде по-нагоре, че дължината е > 0, но пак е малко рисковано, че можеш да извикаш функцията при невалидни условия.

fn trim_white_chars(working_string: &str) -> String {
let mut result = String::new();
let mut string_iterator = working_string.chars();
loop {
match string_iterator.next() {
Some(c) => {
if false == c.is_whitespace() {
result.push(c);
}
},
None => break,
}
}
result
}
fn camelcase_connector(character: &Option<char>) -> String {
if *character == None {
String::new()
}
else {
character.unwrap().to_uppercase().to_string()
}
}
fn kebabcase_connector(character: &Option<char>) -> String {
if *character == None {
String::new()
}
else {
let result = format!("-{}", character.unwrap().to_lowercase());
result
}
}
fn underscore_connector(character: &Option<char>) -> String {
if *character == None {
String::new()
}
else {
let result = format!("_{}", character.unwrap().to_lowercase());
result
}
}
fn screaming_snakecase_connector(character: &Option<char>) -> String {
if *character == None {
String::new()
}
else {
let result = format!("_{}", character.unwrap().to_uppercase());
result
}
}
}

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

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

running 0 tests

test result: ok. 0 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 ... ok
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 ... FAILED
test solution_test::test_whitespace ... ok

failures:

---- solution_test::test_validity stdout ----
thread 'solution_test::test_validity' panicked at 'assertion failed: CodeIdentifier::new("some-var").is_none()', tests/solution_test.rs:9:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.


failures:
    solution_test::test_validity

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

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

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

Димитър качи първо решение на 25.10.2018 02:31 (преди почти 5 години)

Интересно решение, както си го и обясни на лекции :). Помага ти да опростиш доста индивидуалните функции, което може би значи, че биха могли да се унифицират още повече като само инициираш първия символ и подадеш connector функцията като callback. Още не сме говорили за callbacks, де :).

Едната ти грешка в тестовете идва от това, че си твърде permissive -- за "валиден символ" си решил, че е и "-", а той не е -- само подчертавката е валиден separator за входен низ.