|
| 1 | +use std::str::Chars; |
| 2 | + |
| 3 | +use genco::{prelude::*, tokens::ItemStr}; |
| 4 | + |
| 5 | +/// Represents a Go identifier with appropriate casing rules. |
| 6 | +/// |
| 7 | +/// Go identifiers follow specific naming conventions: |
| 8 | +/// - Public identifiers start with uppercase (exported) |
| 9 | +/// - Private identifiers start with lowercase (unexported) |
| 10 | +/// - Local identifiers are used as-is without transformation |
| 11 | +#[derive(Debug, Clone, Copy)] |
| 12 | +pub enum GoIdentifier<'a> { |
| 13 | + /// Public/exported identifier (will be converted to UpperCamelCase) |
| 14 | + Public { name: &'a str }, |
| 15 | + /// Private/unexported identifier (will be converted to lowerCamelCase) |
| 16 | + Private { name: &'a str }, |
| 17 | + /// Local identifier (will be converted to lowerCamelCase) |
| 18 | + Local { name: &'a str }, |
| 19 | +} |
| 20 | + |
| 21 | +impl<'a> GoIdentifier<'a> { |
| 22 | + /// Creates a new public identifier. |
| 23 | + pub fn public(name: &'a str) -> Self { |
| 24 | + Self::Public { name } |
| 25 | + } |
| 26 | + |
| 27 | + /// Creates a new private identifier. |
| 28 | + pub fn private(name: &'a str) -> Self { |
| 29 | + Self::Private { name } |
| 30 | + } |
| 31 | + |
| 32 | + /// Creates a new local identifier. |
| 33 | + pub fn local<'b: 'a>(name: &'a str) -> Self { |
| 34 | + Self::Local { name } |
| 35 | + } |
| 36 | + |
| 37 | + /// Returns an iterator over the characters of the underlying name. |
| 38 | + /// |
| 39 | + /// This provides access to the raw name without case transformations. |
| 40 | + /// |
| 41 | + /// # Returns |
| 42 | + /// An iterator over the characters of the identifier's name. |
| 43 | + pub fn chars(&self) -> Chars<'a> { |
| 44 | + match self { |
| 45 | + GoIdentifier::Public { name } => name.chars(), |
| 46 | + GoIdentifier::Private { name } => name.chars(), |
| 47 | + GoIdentifier::Local { name } => name.chars(), |
| 48 | + } |
| 49 | + } |
| 50 | +} |
| 51 | + |
| 52 | +impl From<GoIdentifier<'_>> for String { |
| 53 | + fn from(value: GoIdentifier) -> Self { |
| 54 | + let mut tokens: Tokens<Go> = Tokens::new(); |
| 55 | + value.format_into(&mut tokens); |
| 56 | + tokens.to_string().expect("to format correctly") |
| 57 | + } |
| 58 | +} |
| 59 | + |
| 60 | +impl FormatInto<Go> for &GoIdentifier<'_> { |
| 61 | + fn format_into(self, tokens: &mut Tokens<Go>) { |
| 62 | + let mut chars = self.chars(); |
| 63 | + |
| 64 | + // TODO(#12): Check for invalid first character |
| 65 | + |
| 66 | + if let GoIdentifier::Public { .. } = self { |
| 67 | + // https://stackoverflow.com/a/38406885 |
| 68 | + match chars.next() { |
| 69 | + Some(c) => tokens.append(ItemStr::from(c.to_uppercase().to_string())), |
| 70 | + None => panic!("No function name"), |
| 71 | + }; |
| 72 | + }; |
| 73 | + |
| 74 | + while let Some(c) = chars.next() { |
| 75 | + match c { |
| 76 | + ' ' | '-' | '_' => { |
| 77 | + if let Some(c) = chars.next() { |
| 78 | + tokens.append(ItemStr::from(c.to_uppercase().to_string())); |
| 79 | + } |
| 80 | + } |
| 81 | + _ => tokens.append(ItemStr::from(c.to_string())), |
| 82 | + } |
| 83 | + } |
| 84 | + } |
| 85 | +} |
| 86 | +impl FormatInto<Go> for GoIdentifier<'_> { |
| 87 | + fn format_into(self, tokens: &mut Tokens<Go>) { |
| 88 | + (&self).format_into(tokens) |
| 89 | + } |
| 90 | +} |
| 91 | + |
| 92 | +#[cfg(test)] |
| 93 | +mod tests { |
| 94 | + |
| 95 | + use genco::{prelude::*, tokens::Tokens}; |
| 96 | + |
| 97 | + use crate::go::GoIdentifier; |
| 98 | + |
| 99 | + #[test] |
| 100 | + fn test_public_identifier() { |
| 101 | + let id = GoIdentifier::public("hello-world"); |
| 102 | + let mut tokens = Tokens::<Go>::new(); |
| 103 | + (&id).format_into(&mut tokens); |
| 104 | + assert_eq!(tokens.to_string().unwrap(), "HelloWorld"); |
| 105 | + } |
| 106 | + |
| 107 | + #[test] |
| 108 | + fn test_private_identifier() { |
| 109 | + let id = GoIdentifier::private("hello-world"); |
| 110 | + let mut tokens = Tokens::<Go>::new(); |
| 111 | + (&id).format_into(&mut tokens); |
| 112 | + assert_eq!(tokens.to_string().unwrap(), "helloWorld"); |
| 113 | + } |
| 114 | + |
| 115 | + #[test] |
| 116 | + fn test_local_identifier() { |
| 117 | + let id = GoIdentifier::local("hello-world"); |
| 118 | + let mut tokens = Tokens::<Go>::new(); |
| 119 | + (&id).format_into(&mut tokens); |
| 120 | + assert_eq!(tokens.to_string().unwrap(), "helloWorld"); |
| 121 | + } |
| 122 | +} |
0 commit comments