1package Mojo::ByteStream;
2use Mojo::Base -strict;
3use overload bool => sub {1}, '""' => sub { ${$_[0]} }, fallback => 1;
4
5use Exporter qw(import);
6use Mojo::Collection;
7use Mojo::Util;
8
9our @EXPORT_OK = ('b');
10
11# Turn most functions from Mojo::Util into methods
12my @UTILS = (
13  qw(b64_decode b64_encode camelize decamelize gunzip gzip hmac_sha1_sum html_unescape humanize_bytes md5_bytes),
14  qw(md5_sum punycode_decode punycode_encode quote sha1_bytes sha1_sum slugify term_escape trim unindent unquote),
15  qw(url_escape url_unescape xml_escape xor_encode)
16);
17for my $name (@UTILS) {
18  my $sub = Mojo::Util->can($name);
19  Mojo::Util::monkey_patch __PACKAGE__, $name, sub {
20    my $self = shift;
21    $$self = $sub->($$self, @_);
22    return $self;
23  };
24}
25
26sub b { __PACKAGE__->new(@_) }
27
28sub clone { $_[0]->new(${$_[0]}) }
29
30sub decode { shift->_delegate(\&Mojo::Util::decode, @_) }
31sub encode { shift->_delegate(\&Mojo::Util::encode, @_) }
32
33sub new {
34  my $class = shift;
35  return bless \(my $dummy = join '', @_), ref $class || $class;
36}
37
38sub say {
39  my ($self, $handle) = @_;
40  $handle ||= \*STDOUT;
41  say $handle $$self;
42  return $self;
43}
44
45sub secure_compare { Mojo::Util::secure_compare ${shift()}, shift }
46
47sub size { length ${$_[0]} }
48
49sub split {
50  my ($self, $pat, $lim) = (shift, shift, shift // 0);
51  return Mojo::Collection->new(map { $self->new($_) } split $pat, $$self, $lim);
52}
53
54sub tap { shift->Mojo::Base::tap(@_) }
55
56sub to_string { ${$_[0]} }
57
58sub with_roles { shift->Mojo::Base::with_roles(@_) }
59
60sub _delegate {
61  my ($self, $sub) = (shift, shift);
62  $$self = $sub->(shift || 'UTF-8', $$self);
63  return $self;
64}
65
661;
67
68=encoding utf8
69
70=head1 NAME
71
72Mojo::ByteStream - ByteStream
73
74=head1 SYNOPSIS
75
76  use Mojo::ByteStream;
77
78  # Manipulate bytestream
79  my $stream = Mojo::ByteStream->new('foo_bar_baz');
80  say $stream->camelize;
81
82  # Chain methods
83  my $stream = Mojo::ByteStream->new('foo bar baz')->quote;
84  $stream = $stream->unquote->encode('UTF-8')->b64_encode('');
85  say "$stream";
86
87  # Use the alternative constructor
88  use Mojo::ByteStream qw(b);
89  my $stream = b('foobarbaz')->b64_encode('')->say;
90
91=head1 DESCRIPTION
92
93L<Mojo::ByteStream> is a scalar-based container for bytestreams that provides a more friendly API for many of the
94functions in L<Mojo::Util>.
95
96  # Access scalar directly to manipulate bytestream
97  my $stream = Mojo::ByteStream->new('foo');
98  $$stream .= 'bar';
99
100=head1 FUNCTIONS
101
102L<Mojo::ByteStream> implements the following functions, which can be imported individually.
103
104=head2 b
105
106  my $stream = b('test123');
107
108Construct a new scalar-based L<Mojo::ByteStream> object.
109
110=head1 METHODS
111
112L<Mojo::ByteStream> implements the following methods.
113
114=head2 b64_decode
115
116  $stream = $stream->b64_decode;
117
118Base64 decode bytestream with L<Mojo::Util/"b64_decode">.
119
120=head2 b64_encode
121
122  $stream = $stream->b64_encode;
123  $stream = $stream->b64_encode("\n");
124
125Base64 encode bytestream with L<Mojo::Util/"b64_encode">.
126
127  # "Zm9vIGJhciBiYXo="
128  b('foo bar baz')->b64_encode('');
129
130=head2 camelize
131
132  $stream = $stream->camelize;
133
134Camelize bytestream with L<Mojo::Util/"camelize">.
135
136=head2 clone
137
138  my $stream2 = $stream->clone;
139
140Return a new L<Mojo::ByteStream> object cloned from this bytestream.
141
142=head2 decamelize
143
144  $stream = $stream->decamelize;
145
146Decamelize bytestream with L<Mojo::Util/"decamelize">.
147
148=head2 decode
149
150  $stream = $stream->decode;
151  $stream = $stream->decode('iso-8859-1');
152
153Decode bytestream with L<Mojo::Util/"decode">, defaults to using C<UTF-8>.
154
155  # "♥"
156  b('%E2%99%A5')->url_unescape->decode;
157
158=head2 encode
159
160  $stream = $stream->encode;
161  $stream = $stream->encode('iso-8859-1');
162
163Encode bytestream with L<Mojo::Util/"encode">, defaults to using C<UTF-8>.
164
165  # "%E2%99%A5"
166  b('♥')->encode->url_escape;
167
168=head2 gunzip
169
170  $stream = $stream->gunzip;
171
172Uncompress bytestream with L<Mojo::Util/"gunzip">.
173
174=head2 gzip
175
176  stream = $stream->gzip;
177
178Compress bytestream with L<Mojo::Util/"gzip">.
179
180=head2 hmac_sha1_sum
181
182  $stream = $stream->hmac_sha1_sum('passw0rd');
183
184Generate HMAC-SHA1 checksum for bytestream with L<Mojo::Util/"hmac_sha1_sum">.
185
186  # "7fbdc89263974a89210ea71f171c77d3f8c21471"
187  b('foo bar baz')->hmac_sha1_sum('secr3t');
188
189=head2 html_unescape
190
191  $stream = $stream->html_unescape;
192
193Unescape all HTML entities in bytestream with L<Mojo::Util/"html_unescape">.
194
195  # "%3Chtml%3E"
196  b('&lt;html&gt;')->html_unescape->url_escape;
197
198=head2 humanize_bytes
199
200  $stream = $stream->humanize_bytes;
201
202Turn number of bytes into a simplified human readable format for bytestream with L<Mojo::Util/"humanize_bytes">.
203
204=head2 md5_bytes
205
206  $stream = $stream->md5_bytes;
207
208Generate binary MD5 checksum for bytestream with L<Mojo::Util/"md5_bytes">.
209
210=head2 md5_sum
211
212  $stream = $stream->md5_sum;
213
214Generate MD5 checksum for bytestream with L<Mojo::Util/"md5_sum">.
215
216=head2 new
217
218  my $stream = Mojo::ByteStream->new('test123');
219
220Construct a new scalar-based L<Mojo::ByteStream> object.
221
222=head2 punycode_decode
223
224  $stream = $stream->punycode_decode;
225
226Punycode decode bytestream with L<Mojo::Util/"punycode_decode">.
227
228=head2 punycode_encode
229
230  $stream = $stream->punycode_encode;
231
232Punycode encode bytestream with L<Mojo::Util/"punycode_encode">.
233
234=head2 quote
235
236  $stream = $stream->quote;
237
238Quote bytestream with L<Mojo::Util/"quote">.
239
240=head2 say
241
242  $stream = $stream->say;
243  $stream = $stream->say(*STDERR);
244
245Print bytestream to handle and append a newline, defaults to using C<STDOUT>.
246
247=head2 secure_compare
248
249  my $bool = $stream->secure_compare($str);
250
251Compare bytestream with L<Mojo::Util/"secure_compare">.
252
253=head2 sha1_bytes
254
255  $stream = $stream->sha1_bytes;
256
257Generate binary SHA1 checksum for bytestream with L<Mojo::Util/"sha1_bytes">.
258
259=head2 sha1_sum
260
261  $stream = $stream->sha1_sum;
262
263Generate SHA1 checksum for bytestream with L<Mojo::Util/"sha1_sum">.
264
265=head2 size
266
267  my $size = $stream->size;
268
269Size of bytestream.
270
271=head2 slugify
272
273  $stream = $stream->slugify;
274  $stream = $stream->slugify($bool);
275
276Generate URL slug for bytestream with L<Mojo::Util/"slugify">.
277
278=head2 split
279
280  my $collection = $stream->split(',');
281  my $collection = $stream->split(',', -1);
282
283Turn bytestream into L<Mojo::Collection> object containing L<Mojo::ByteStream> objects.
284
285  # "One,Two,Three"
286  b("one,two,three")->split(',')->map('camelize')->join(',');
287
288  # "One,Two,Three,,,"
289  b("one,two,three,,,")->split(',', -1)->map('camelize')->join(',');
290
291=head2 tap
292
293  $stream = $stream->tap(sub {...});
294
295Alias for L<Mojo::Base/"tap">.
296
297=head2 term_escape
298
299  $stream = $stream->term_escape;
300
301Escape POSIX control characters in bytestream with L<Mojo::Util/"term_escape">.
302
303  # Print binary checksum to terminal
304  b('foo')->sha1_bytes->term_escape->say;
305
306=head2 to_string
307
308  my $str = $stream->to_string;
309
310Stringify bytestream.
311
312=head2 trim
313
314  $stream = $stream->trim;
315
316Trim whitespace characters from both ends of bytestream with L<Mojo::Util/"trim">.
317
318=head2 unindent
319
320  $stream = $stream->unindent;
321
322Unindent bytestream with L<Mojo::Util/"unindent">.
323
324=head2 unquote
325
326  $stream = $stream->unquote;
327
328Unquote bytestream with L<Mojo::Util/"unquote">.
329
330=head2 url_escape
331
332  $stream = $stream->url_escape;
333  $stream = $stream->url_escape('^A-Za-z0-9\-._~');
334
335Percent encode all unsafe characters in bytestream with L<Mojo::Util/"url_escape">.
336
337  # "%E2%98%83"
338  b('☃')->encode->url_escape;
339
340=head2 url_unescape
341
342  $stream = $stream->url_unescape;
343
344Decode percent encoded characters in bytestream with L<Mojo::Util/"url_unescape">.
345
346  # "&lt;html&gt;"
347  b('%3Chtml%3E')->url_unescape->xml_escape;
348
349=head2 with_roles
350
351  my $new_class = Mojo::ByteStream->with_roles('Mojo::ByteStream::Role::One');
352  my $new_class = Mojo::ByteStream->with_roles('+One', '+Two');
353  $stream       = $stream->with_roles('+One', '+Two');
354
355Alias for L<Mojo::Base/"with_roles">.
356
357=head2 xml_escape
358
359  $stream = $stream->xml_escape;
360
361Escape only the characters C<&>, C<E<lt>>, C<E<gt>>, C<"> and C<'> in bytestream with L<Mojo::Util/"xml_escape">.
362
363=head2 xor_encode
364
365  $stream = $stream->xor_encode($key);
366
367XOR encode bytestream with L<Mojo::Util/"xor_encode">.
368
369  # "%04%0E%15B%03%1B%10"
370  b('foo bar')->xor_encode('baz')->url_escape;
371
372=head1 OPERATORS
373
374L<Mojo::ByteStream> overloads the following operators.
375
376=head2 bool
377
378  my $bool = !!$bytestream;
379
380Always true.
381
382=head2 stringify
383
384  my $str = "$bytestream";
385
386Alias for L</"to_string">.
387
388=head1 SEE ALSO
389
390L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
391
392=cut
393