Page MenuHomePhabricator

D14099.id.diff
No OneTemporary

D14099.id.diff

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

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)

Event Timeline