Created
April 30, 2014 01:11
-
-
Save simonwelsh/b926dc1e8929529c9724 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?hh // strict | |
namespace beatbox; | |
type Path = ImmVector<string>; | |
type Extension = ?string; | |
type Metadata = Map<string, mixed>; | |
type FragmentHandler = (function(Path, Extension, Metadata): mixed); | |
type CheckerCallback = (function(string, Metadata): bool); | |
type FragmentTable = Map<string, FragmentHandler>; | |
type CheckerTable = Map<string, CheckerCallback>; | |
type PathTable = Pair<FragmentTable, Metadata>; | |
type RouteTable = Map<string, PathTable>; | |
type SimpleRouteTable = Map<string, FragmentTable>; | |
newtype RequestStackItem = (Path, Extension, FragmentTable, Metadata); | |
newtype RequestStack = Vector<RequestStackItem>; | |
class Router { | |
protected static RouteTable $_straight_routes = Map {}; | |
protected static RouteTable $_regex_routes = Map {}; | |
protected static CheckerTable $_checkers = Map {}; | |
protected static RequestStack $_stack = Vector {}; | |
/** | |
* Route the url, generating the fragments | |
*/ | |
public static function route(string $url, \Traversable<string> $fragments = Vector {}) : mixed { | |
// Get extension | |
if (strpos($url, '.') === false) { | |
$ext = ''; | |
} else { | |
list($url, $ext) = explode($url, '.', 2); | |
} | |
// Get URL parts | |
$url = trim($url, '/'); | |
$parts = explode($url, '/') ?: ['/']; | |
$parts = Vector::fromArray($parts); | |
// Get fragments | |
$fragments = new ImmVector($fragments); | |
if ($fragments->count() == 0) { | |
$fragments = ImmVector {'page'}; | |
} | |
// Get potential routes and MD | |
$url = $parts->toImmVector(); | |
$allPaths = Vector {}; | |
do { | |
$path = implode('/', $parts); | |
$allPaths[] = $path; | |
$parts->pop(); | |
} while(!$parts->isEmpty()); | |
$allPaths[] = ''; | |
$allPaths->reverse(); | |
$paths = Map {}; | |
$metadata = Map {}; | |
$allPaths->map((string $path) ==> { | |
$p = static::get_routes_for_path($path); | |
if ($p) { | |
$paths->setAll($p[0]); | |
$metadata->setAll($p[1]); | |
} | |
}); | |
self::$_stack->add(tuple($url, $ext, $paths, $metadata)); | |
} | |
/** | |
* Add routes with metadata | |
*/ | |
public static function add_routes(RouteTable $routes, bool $regex = false) : void { | |
if ($regex) { | |
$map = self::$_regex_routes; | |
} else { | |
$map = self::$_straight_routes; | |
} | |
foreach ($routes as $path => $table) { | |
$path = self::sanitise_path($path); | |
if (!$map->contains($path)) { | |
$map[$path] = Pair { Map {}, Map {} }; | |
} | |
// Add the fragment handlers | |
$map[$path][0]->setAll($table[0]); | |
// Add the metadata | |
$map[$path][1]->setAll($table[1]); | |
} | |
} | |
/** | |
* Add routes without metadata | |
*/ | |
public static function add_simple_routes(SimpleRouteTable $routes, bool $regex = false): void { | |
static::add_routes($routes->map((FragmentTable $t) ==> Pair {$t, Map {} }), $regex); | |
} | |
/** | |
* Gets the routes available for a given path | |
*/ | |
public static function get_routes_for_path(string $path): ?PathTable { | |
$path = self::sanitise_path($path); | |
if (self::$_straight_routes->contains($path)) { | |
return self::$_straight_routes[$path]; | |
} | |
if ($path) { | |
foreach (self::$_regex_routes as $p => $table) { | |
if (preg_match('#^' . $p . '$#', $path)) { | |
return $table; | |
} | |
} | |
} | |
return null; | |
} | |
/** | |
* Add a checker for the given metadata key | |
*/ | |
public static function add_checker(string $key, CheckerCallback $callback) : void { | |
self::$_checkers[strtolower($key)] = $callback; | |
} | |
/** | |
* Return the checker for the given metadata key | |
*/ | |
public static function get_checker(string $key) : ?CheckerCallback { | |
return self::$_checkers->get(strtolower($key)); | |
} | |
public static function response_for_fragment(string $frag) : mixed { | |
} | |
public static function current_path() : ?ImmVector<string> { | |
if (!self::$_stack->isEmpty()) { | |
$index = self::$_stack->count() - 1; | |
return self::$_stack[$index][0]; | |
} | |
return null; | |
} | |
protected static function reset(): void { | |
self::$_straight_routes = Map {}; | |
self::$_regex_routes = Map {}; | |
self::$_checkers = Map {}; | |
self::$_stack = Vector {}; | |
} | |
private static function sanitise_path(string $path): string { | |
if (strlen($path)) { | |
$path = trim($path, '/') ?: '/'; | |
} | |
return $path; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment