Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F13218736
D14099.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
7 KB
Referenced Files
None
Subscribers
None
D14099.id.diff
View Options
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -135,6 +135,8 @@
'PhutilConsoleTable' => 'console/PhutilConsoleTable.php',
'PhutilConsoleWrapTestCase' => 'console/__tests__/PhutilConsoleWrapTestCase.php',
'PhutilContextFreeGrammar' => 'grammar/PhutilContextFreeGrammar.php',
+ 'PhutilCowsay' => 'utils/PhutilCowsay.php',
+ 'PhutilCowsayTestCase' => 'utils/__tests__/PhutilCowsayTestCase.php',
'PhutilCsprintfTestCase' => 'xsprintf/__tests__/PhutilCsprintfTestCase.php',
'PhutilCzechLocale' => 'internationalization/locales/PhutilCzechLocale.php',
'PhutilDaemon' => 'daemon/PhutilDaemon.php',
@@ -643,6 +645,8 @@
'PhutilConsoleTable' => 'Phobject',
'PhutilConsoleWrapTestCase' => 'PhutilTestCase',
'PhutilContextFreeGrammar' => 'Phobject',
+ 'PhutilCowsay' => 'Phobject',
+ 'PhutilCowsayTestCase' => 'PhutilTestCase',
'PhutilCsprintfTestCase' => 'PhutilTestCase',
'PhutilCzechLocale' => 'PhutilLocale',
'PhutilDaemon' => 'Phobject',
diff --git a/src/utils/PhutilCowsay.php b/src/utils/PhutilCowsay.php
new file mode 100644
--- /dev/null
+++ b/src/utils/PhutilCowsay.php
@@ -0,0 +1,135 @@
+<?php
+
+/**
+ * Parser and renderer for ".cow" files used by the `cowsay` program.
+ */
+final class PhutilCowsay extends Phobject {
+
+ private $template;
+ private $eyes = 'oo';
+ private $tongue = ' ';
+ private $action = 'say';
+ private $text;
+
+ public function setTemplate($template) {
+ $this->template = $template;
+ return $this;
+ }
+
+ public function setEyes($eyes) {
+ $this->eyes = $eyes;
+ return $this;
+ }
+
+ public function setTongue($tongue) {
+ $this->tongue = $tongue;
+ return $this;
+ }
+
+ public function setAction($action) {
+ $this->action = $action;
+ return $this;
+ }
+
+ public function setText($text) {
+ $this->text = $text;
+ return $this;
+ }
+
+ public function renderCow() {
+ $width = 40;
+ $template = $this->template;
+
+ // Real ".cow" files are Perl scripts which define a variable called
+ // "$the_cow". We aren't going to interpret Perl, so strip all this stuff
+ // (and any comments in the file) away.
+ $template = phutil_split_lines($template, true);
+ $keep = array();
+ foreach ($template as $key => $line) {
+ if (preg_match('/^#/', $line)) {
+ continue;
+ }
+ if (preg_match('/^\s*\\$the_cow/', $line)) {
+ continue;
+ }
+ if (preg_match('/^\s*EOC\s*$/', $line)) {
+ continue;
+ }
+ $keep[] = $line;
+ }
+ $template = implode('', $keep);
+
+ $template = preg_replace_callback(
+ '/\\$([a-z]+)/',
+ array($this, 'replaceTemplateVariable'),
+ $template);
+ if ($template === false) {
+ throw new Exception(
+ pht(
+ 'Failed to replace template variables while rendering cow!'));
+ }
+
+ $lines = $this->text;
+
+ // TODO: It would be nice to use a utf8 soft wrap here instead, but we
+ // do not currently have one. Soft wrap first, then force to utf8.
+ $lines = wordwrap($lines, $width - 4, "\n", true);
+ $lines = phutil_split_lines($lines, false);
+ foreach ($lines as $key => $line) {
+ $lines[$key] = phutil_utf8ize($line);
+ }
+
+ if ($this->action == 'think') {
+ $borders = '((()))';
+ } else {
+ if (count($lines) == 1) {
+ $borders = '<<<>>>';
+ } else {
+ $borders = '/|\\\\|/';
+ }
+ }
+
+ $size = 0;
+ foreach ($lines as $line) {
+ $size = max(strlen($line), $size);
+ }
+
+ $balloon = array();
+ $balloon[] = ' '.str_repeat('_', $size + 2);
+ $lines = array_values($lines);
+ $last = (count($lines) - 1);
+ foreach ($lines as $idx => $line) {
+ if ($idx == 0) {
+ $l = $borders[0];
+ $r = $borders[3];
+ } else if ($idx == $last) {
+ $l = $borders[2];
+ $r = $borders[5];
+ } else {
+ $l = $borders[1];
+ $r = $borders[4];
+ }
+ $balloon[] = $l.' '.str_pad($line, $size).' '.$r;
+
+ }
+ $balloon[] = ' '.str_repeat('-', $size + 2);
+ $balloon = implode("\n", $balloon);
+
+ return rtrim($balloon."\n".$template);
+ }
+
+ public function replaceTemplateVariable($matches) {
+ switch ($matches[1]) {
+ case 'eyes':
+ return str_pad($this->eyes, 2);
+ case 'tongue':
+ return str_pad($this->tongue, 2);
+ case 'thoughts':
+ return ($this->action == 'say' ? '\\' : 'o');
+ default:
+ return $matches[0];
+ }
+ }
+
+
+}
diff --git a/src/utils/__tests__/PhutilCowsayTestCase.php b/src/utils/__tests__/PhutilCowsayTestCase.php
new file mode 100644
--- /dev/null
+++ b/src/utils/__tests__/PhutilCowsayTestCase.php
@@ -0,0 +1,60 @@
+<?php
+
+final class PhutilCowsayTestCase extends PhutilTestCase {
+
+ public function testCowsay() {
+ $dir = dirname(__FILE__).'/cowsay/';
+ $tests = array();
+ foreach (Filesystem::listDirectory($dir, false) as $test) {
+ if (preg_match('/\.test\z/', $test)) {
+ $tests[] = $test;
+ }
+ }
+
+ foreach ($tests as $test) {
+ $data = Filesystem::readFile($dir.$test);
+ $expect = preg_replace('/\.test\z/', '.expect', $test);
+ $expect = Filesystem::readFile($dir.$expect);
+
+ $pattern = '/^~~~+$/m';
+
+ if (!preg_match($pattern, $data)) {
+ throw new Exception(
+ pht(
+ 'Expected cowsay test file "%s" to contain a line of tildes '.
+ 'separating the template from the test parameters.',
+ $test));
+ }
+
+ list($template, $spec) = preg_split($pattern, $data, 2);
+ $spec = phutil_json_decode($spec);
+
+ $cowsay = id(new PhutilCowsay())
+ ->setTemplate($template);
+
+ if (idx($spec, 'text') !== null) {
+ $cowsay->setText($spec['text']);
+ }
+
+ if (idx($spec, 'eyes') !== null) {
+ $cowsay->setEyes($spec['eyes']);
+ }
+
+ if (idx($spec, 'tongue') !== null) {
+ $cowsay->setTongue($spec['tongue']);
+ }
+
+ if (idx($spec, 'action') !== null) {
+ $cowsay->setAction($spec['action']);
+ }
+
+ $actual = $cowsay->renderCow();
+
+ $this->assertEqual(
+ rtrim($expect),
+ rtrim($actual),
+ pht('Cowsay Test Case "%s"', $test));
+ }
+ }
+
+}
diff --git a/src/utils/__tests__/cowsay/cube.expect b/src/utils/__tests__/cowsay/cube.expect
new file mode 100644
--- /dev/null
+++ b/src/utils/__tests__/cowsay/cube.expect
@@ -0,0 +1,12 @@
+ __________________
+( I made a friend! )
+ ------------------
+ o
+ o
+ /---\__/---\\
+ | / .... \ ||
+ \ ..--.. //
+ |..(<3). ||
+ / ..--.. \\
+ | \ .... / ||
+ \---/--\---//
diff --git a/src/utils/__tests__/cowsay/cube.test b/src/utils/__tests__/cowsay/cube.test
new file mode 100644
--- /dev/null
+++ b/src/utils/__tests__/cowsay/cube.test
@@ -0,0 +1,15 @@
+ $thoughts
+ $thoughts
+ /---\__/---\\
+ | / .... \ ||
+ \ ..--.. //
+ |..($eyes). ||
+ / ..--.. \\
+ | \ .... / ||
+ \---/--\---//
+~~~~~~~~~~
+{
+ "text": "I made a friend!",
+ "action": "think",
+ "eyes": "<3"
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
May 19 2024, 1:14 PM (5 w, 2 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6286381
Default Alt Text
D14099.id.diff (7 KB)
Attached To
Mode
D14099: Implement Cowsay in PHP
Attached
Detach File
Event Timeline
Log In to Comment