Property definitions

popcorn $ AppParamRoute :: defaultinit
# Parameterizable routes.
#
# Routes that can contains variables parts that will be resolved during the
# matching process.
#
# Route parameters are marked with a colon `:`
# ~~~
# var route = new AppParamRoute("/:id")
# assert not route.match("/")
# assert route.match("/user")
# assert route.match("/user/")
# assert not route.match("/user/10")
# ~~~
#
# It is possible to use more than one parameter in the same route:
# ~~~
# route = new AppParamRoute("/user/:userId/items/:itemId")
# assert not route.match("/user/10/items/")
# assert route.match("/user/10/items/e895346")
# assert route.match("/user/USER/items/0/")
# assert not route.match("/user/10/items/10/profile")
# ~~~
class AppParamRoute
	super AppRoute

	init do parse_path_parameters(path)

	# Cut `path` into `UriParts`.
	fun parse_path_parameters(path: String) do
		for part in path.split("/") do
			if not part.is_empty and part.first == ':' then
				# is an uri param
				path_parts.add new UriParam(part.substring(1, part.length))
			else
				# is a standard string
				path_parts.add new UriString(part)
			end
		end
	end

	# For parameterized routes, parameter names are replaced by their value in the URI.
	# ~~~
	# var route = new AppParamRoute("/user/:id")
	# assert route.resolve_path("/user/10/profile") == "/user/10"
	#
	# route = new AppParamRoute("/user/:userId/items/:itemId")
	# assert route.resolve_path("/user/Morriar/items/i156/desc") == "/user/Morriar/items/i156"
	# ~~~
	redef fun resolve_path(uri) do
		var uri_params = parse_uri_parameters(uri)
		var path = "/"
		for part in path_parts do
			if part isa UriString then
				path /= part.string
			else if part isa UriParam then
				path /= uri_params.get_or_default(part.name, part.name)
			end
		end
		return path.simplify_path
	end

	# Extract parameter values from `uri`.
	# ~~~
	# var route = new AppParamRoute("/user/:userId/items/:itemId")
	# var params = route.parse_uri_parameters("/user/10/items/i125/desc")
	# assert params["userId"] == "10"
	# assert params["itemId"] == "i125"
	# assert params.length == 2
	#
	# params = route.parse_uri_parameters("/")
	# assert params.is_empty
	# ~~~
	fun parse_uri_parameters(uri: String): Map[String, String] do
		var res = new HashMap[String, String]
		if path_parts.is_empty then return res
		var parts = uri.split("/")
		for i in [0 .. path_parts.length[ do
			if i >= parts.length then return res
			var ppart = path_parts[i]
			var part = parts[i]
			if not ppart.match(part) then return res
			if ppart isa UriParam then
				res[ppart.name] = part
			end
		end
		return res
	end

	private var path_parts = new Array[UriPart]
end
lib/popcorn/pop_routes.nit:87,1--176,3