github.com/enetx/g@v1.0.80/string_encdec.go (about)

     1  package g
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/hex"
     6  	"encoding/json"
     7  	"encoding/xml"
     8  	"html"
     9  	"net/url"
    10  	"strconv"
    11  )
    12  
    13  type (
    14  	// A struct that wraps an String for encoding.
    15  	enc struct{ str String }
    16  
    17  	// A struct that wraps an String for decoding.
    18  	dec struct{ str String }
    19  )
    20  
    21  // Enc returns an enc struct wrapping the given String.
    22  func (s String) Enc() enc { return enc{s} }
    23  
    24  // Dec returns a dec struct wrapping the given String.
    25  func (s String) Dec() dec { return dec{s} }
    26  
    27  // Base64 encodes the wrapped String using Base64 and returns the encoded result as an String.
    28  func (e enc) Base64() String { return String(base64.StdEncoding.EncodeToString(e.str.ToBytes())) }
    29  
    30  // Base64 decodes the wrapped String using Base64 and returns the decoded result as an String.
    31  func (d dec) Base64() Result[String] {
    32  	decoded, err := base64.StdEncoding.DecodeString(d.str.Std())
    33  	if err != nil {
    34  		return Err[String](err)
    35  	}
    36  
    37  	return Ok(String(decoded))
    38  }
    39  
    40  // JSON encodes the provided data as JSON and returns the result as an String.
    41  func (enc) JSON(data any) Result[String] {
    42  	jsonData, err := json.Marshal(data)
    43  	if err != nil {
    44  		return Err[String](err)
    45  	}
    46  
    47  	return Ok(String(jsonData))
    48  }
    49  
    50  // JSON decodes the wrapped String using JSON and unmarshals it into the provided data object.
    51  func (d dec) JSON(data any) Result[String] {
    52  	err := json.Unmarshal(d.str.ToBytes(), data)
    53  	if err != nil {
    54  		return Err[String](err)
    55  	}
    56  
    57  	return Ok(d.str)
    58  }
    59  
    60  // XML encodes the provided data as XML and returns the result as an String.
    61  // The optional prefix and indent String values can be provided for XML indentation.
    62  func (enc) XML(data any, s ...String) Result[String] {
    63  	var (
    64  		prefix string
    65  		indent string
    66  	)
    67  
    68  	if len(s) > 1 {
    69  		prefix = s[0].Std()
    70  		indent = s[1].Std()
    71  	}
    72  
    73  	xmlData, err := xml.MarshalIndent(data, prefix, indent)
    74  	if err != nil {
    75  		return Err[String](err)
    76  	}
    77  
    78  	return Ok(String(xmlData))
    79  }
    80  
    81  // XML decodes the wrapped String using XML and unmarshals it into the provided data object.
    82  func (d dec) XML(data any) Result[String] {
    83  	err := xml.Unmarshal(d.str.ToBytes(), data)
    84  	if err != nil {
    85  		return Err[String](err)
    86  	}
    87  
    88  	return Ok(d.str)
    89  }
    90  
    91  // URL encodes the input string, escaping reserved characters as per RFC 2396.
    92  // If safe characters are provided, they will not be encoded.
    93  //
    94  // Parameters:
    95  //
    96  // - safe (String): Optional. Characters to exclude from encoding.
    97  // If provided, the function will not encode these characters.
    98  //
    99  // Returns:
   100  //
   101  // - String: Encoded URL string.
   102  func (e enc) URL(safe ...String) String {
   103  	reserved := String(";/?:@&=+$,") // Reserved characters as per RFC 2396
   104  	if len(safe) != 0 {
   105  		reserved = safe[0]
   106  	}
   107  
   108  	enc := NewBuilder()
   109  
   110  	for _, r := range e.str {
   111  		if reserved.ContainsRune(r) {
   112  			enc.WriteRune(r)
   113  			continue
   114  		}
   115  
   116  		enc.Write(String(url.QueryEscape(string(r))))
   117  	}
   118  
   119  	return enc.String()
   120  }
   121  
   122  // URL URL-decodes the wrapped String and returns the decoded result as an String.
   123  func (d dec) URL() Result[String] {
   124  	result, err := url.QueryUnescape(d.str.Std())
   125  	if err != nil {
   126  		return Err[String](err)
   127  	}
   128  
   129  	return Ok(String(result))
   130  }
   131  
   132  // HTML HTML-encodes the wrapped String and returns the encoded result as an String.
   133  func (e enc) HTML() String { return String(html.EscapeString(e.str.Std())) }
   134  
   135  // HTML HTML-decodes the wrapped String and returns the decoded result as an String.
   136  func (d dec) HTML() String { return String(html.UnescapeString(d.str.Std())) }
   137  
   138  // Rot13 encodes the wrapped String using ROT13 cipher and returns the encoded result as an
   139  // String.
   140  func (e enc) Rot13() String {
   141  	rot := func(r rune) rune {
   142  		switch {
   143  		case r >= 'A' && r <= 'Z':
   144  			return 'A' + (r-'A'+13)%26
   145  		case r >= 'a' && r <= 'z':
   146  			return 'a' + (r-'a'+13)%26
   147  		default:
   148  			return r
   149  		}
   150  	}
   151  
   152  	return e.str.Map(rot)
   153  }
   154  
   155  // Rot13 decodes the wrapped String using ROT13 cipher and returns the decoded result as an
   156  // String.
   157  func (d dec) Rot13() String { return d.str.Enc().Rot13() }
   158  
   159  // XOR encodes the wrapped String using XOR cipher with the given key and returns the encoded
   160  // result as an String.
   161  func (e enc) XOR(key String) String {
   162  	if key.Empty() {
   163  		return e.str
   164  	}
   165  
   166  	encrypted := e.str.ToBytes()
   167  
   168  	for i := range len(e.str) {
   169  		encrypted[i] ^= key[i%len(key)]
   170  	}
   171  
   172  	return String(encrypted)
   173  }
   174  
   175  // XOR decodes the wrapped String using XOR cipher with the given key and returns the decoded
   176  // result as an String.
   177  func (d dec) XOR(key String) String { return d.str.Enc().XOR(key) }
   178  
   179  // Hex hex-encodes the wrapped String and returns the encoded result as an String.
   180  func (e enc) Hex() String {
   181  	result := NewBuilder()
   182  	for i := range len(e.str) {
   183  		result.Write(Int(e.str[i]).ToHex())
   184  	}
   185  
   186  	return result.String()
   187  }
   188  
   189  // Hex hex-decodes the wrapped String and returns the decoded result as an String.
   190  func (d dec) Hex() Result[String] {
   191  	result, err := hex.DecodeString(d.str.Std())
   192  	if err != nil {
   193  		return Err[String](err)
   194  	}
   195  
   196  	return Ok(String(result))
   197  }
   198  
   199  // Octal returns the octal representation of the encoded string.
   200  func (e enc) Octal() String {
   201  	result := NewSlice[String](e.str.LenRunes())
   202  	for i, char := range e.str.ToRunes() {
   203  		result.Set(Int(i), Int(char).ToOctal())
   204  	}
   205  
   206  	return result.Join(" ")
   207  }
   208  
   209  // Octal returns the octal representation of the decimal-encoded string.
   210  func (d dec) Octal() Result[String] {
   211  	result := NewBuilder()
   212  
   213  	for _, v := range d.str.Split(" ").Collect() {
   214  		n, err := strconv.ParseUint(v.Std(), 8, 32)
   215  		if err != nil {
   216  			return Err[String](err)
   217  		}
   218  
   219  		result.WriteRune(rune(n))
   220  	}
   221  
   222  	return Ok(result.String())
   223  }
   224  
   225  // Binary converts the wrapped String to its binary representation as an String.
   226  func (e enc) Binary() String {
   227  	result := NewBuilder()
   228  	for i := range len(e.str) {
   229  		result.Write(Int(e.str[i]).ToBinary())
   230  	}
   231  
   232  	return result.String()
   233  }
   234  
   235  // Binary converts the wrapped binary String back to its original String representation.
   236  func (d dec) Binary() Result[String] {
   237  	var result Bytes
   238  
   239  	for i := 0; i+8 <= len(d.str); i += 8 {
   240  		b, err := strconv.ParseUint(d.str[i:i+8].Std(), 2, 8)
   241  		if err != nil {
   242  			return Err[String](err)
   243  		}
   244  
   245  		result = append(result, byte(b))
   246  	}
   247  
   248  	return Ok(result.ToString())
   249  }