mirror of
https://gitea.invidious.io/iv-org/shard-athena-negotiation.git
synced 2024-08-15 00:53:23 +00:00
Scaffold out types and start of implementation
This commit is contained in:
parent
34aeeb58c0
commit
8d6d8cbf79
9 changed files with 184 additions and 1 deletions
57
src/abstract_negotiator.cr
Normal file
57
src/abstract_negotiator.cr
Normal file
|
@ -0,0 +1,57 @@
|
|||
abstract class Athena::Negotiation::AbstractNegotiator
|
||||
private abstract def create_header(header : String) : ANG::BaseAccept
|
||||
|
||||
def best(header : String, priorities : Array(String), strict : Bool = false) : ANG::BaseAccept?
|
||||
raise ArgumentError.new "priorities should not be empty" if priorities.empty?
|
||||
raise ArgumentError.new "The header string should not be empty" if header.blank?
|
||||
|
||||
accepted_headers = Array(ANG::BaseAccept).new
|
||||
|
||||
self.parse_header(header) do |h|
|
||||
accepted_headers << self.create_header h
|
||||
rescue ex
|
||||
raise ex if strict
|
||||
end
|
||||
|
||||
accepted_priorties = priorities.map &->create_header(String)
|
||||
|
||||
matches = self.find_matches accepted_headers, accepted_priorties
|
||||
|
||||
pp matches
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
private def parse_header(header : String, & : String ->) : Nil
|
||||
header.scan /(?:[^,\"]*+(?:"[^"]*+\")?)+[^,\"]*+/ do |match|
|
||||
yield match[0] unless match[0].blank?
|
||||
end
|
||||
end
|
||||
|
||||
private def find_matches(headers : Array(ANG::BaseAccept), priorities : Array(ANG::BaseAccept)) : Array(ANG::AcceptMatch)
|
||||
matches = [] of ANG::AcceptMatch
|
||||
|
||||
priorities.each_with_index do |priority, idx|
|
||||
headers.each do |header|
|
||||
if match = self.match(header, priority, idx)
|
||||
matches << match
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
matches
|
||||
end
|
||||
|
||||
private def match(header : ANG::BaseAccept, priority : ANG::BaseAccept, index : Int32) : ANG::AcceptMatch?
|
||||
accept_type = header.type
|
||||
priority_type = priority.type
|
||||
|
||||
equal = accept_type.downcase <=> priority_type.downcase
|
||||
|
||||
if !equal.zero? || accept_type == "*"
|
||||
return ANG::AcceptMatch.new header.quality * priority.quality, 1 * equal, index
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
end
|
20
src/accept.cr
Normal file
20
src/accept.cr
Normal file
|
@ -0,0 +1,20 @@
|
|||
require "./base_accept"
|
||||
|
||||
struct Athena::Negotiation::Accept < Athena::Negotiation::BaseAccept
|
||||
getter base_part : String
|
||||
getter sub_part : String
|
||||
|
||||
def initialize(value : String)
|
||||
super value
|
||||
|
||||
@type = "*/*" if @type == "*"
|
||||
|
||||
parts = @type.split '/'
|
||||
|
||||
# TODO: Use more specific exception
|
||||
raise "Invalid media type: '#{@type}'." if parts.size != 2 || !parts[0].presence || !parts[1].presence
|
||||
|
||||
@base_part = parts[0]
|
||||
@sub_part = parts[1]
|
||||
end
|
||||
end
|
4
src/accept_charset.cr
Normal file
4
src/accept_charset.cr
Normal file
|
@ -0,0 +1,4 @@
|
|||
require "./base_accept"
|
||||
|
||||
struct Athena::Negotiation::AcceptCharset < Athena::Negotiation::BaseAccept
|
||||
end
|
4
src/accept_encoding.cr
Normal file
4
src/accept_encoding.cr
Normal file
|
@ -0,0 +1,4 @@
|
|||
require "./base_accept"
|
||||
|
||||
struct Athena::Negotiation::AcceptEncoding < Athena::Negotiation::BaseAccept
|
||||
end
|
29
src/accept_language.cr
Normal file
29
src/accept_language.cr
Normal file
|
@ -0,0 +1,29 @@
|
|||
require "./base_accept"
|
||||
|
||||
struct Athena::Negotiation::AcceptLanguage < Athena::Negotiation::BaseAccept
|
||||
getter language : String
|
||||
getter script : String? = nil
|
||||
getter region : String? = nil
|
||||
|
||||
def initialize(value : String)
|
||||
super value
|
||||
|
||||
parts = @value.split '-'
|
||||
|
||||
pp parts
|
||||
|
||||
case parts.size
|
||||
when 2
|
||||
@language = parts[0]
|
||||
@region = parts[1]
|
||||
when 1
|
||||
@language = parts[0]
|
||||
when 3
|
||||
@language = parts[0]
|
||||
@script = parts[1]
|
||||
@region = parts[2]
|
||||
else
|
||||
raise "Invalid language: '#{@value}'."
|
||||
end
|
||||
end
|
||||
end
|
7
src/accept_match.cr
Normal file
7
src/accept_match.cr
Normal file
|
@ -0,0 +1,7 @@
|
|||
struct Athena::Negotiation::AcceptMatch
|
||||
getter quality : Float32
|
||||
getter score : Int32
|
||||
getter index : Int32
|
||||
|
||||
def initialize(@quality : Float32, @score : Int32, @index : Int32); end
|
||||
end
|
|
@ -1,6 +1,26 @@
|
|||
require "./accept"
|
||||
require "./accept_match"
|
||||
require "./accept_charset"
|
||||
require "./accept_encoding"
|
||||
require "./accept_language"
|
||||
require "./negotiator"
|
||||
|
||||
# Convenience alias to make referencing `Athena::Negotiation` types easier.
|
||||
alias ANG = Athena::Negotiation
|
||||
|
||||
module Athena::Negotiation
|
||||
VERSION = "0.1.0"
|
||||
end
|
||||
|
||||
# pp ANG::Accept.new "application/json;q=1.0"
|
||||
# pp ANG::Accept.new "application/json ;q=1.0; level=2;foo= bar"
|
||||
# pp ANG::Accept.new "text/html ; level = 2 ; q = 0.4"
|
||||
|
||||
# puts
|
||||
# puts
|
||||
|
||||
# pp ANG::AcceptLanguage.new "en-gb;q=0.8"
|
||||
|
||||
n = ANG::Negotiator.new
|
||||
|
||||
pp n.best "text/html;level=1", ["text/html"] # text/html
|
||||
pp n.best "text/*;q=0.7, text/html;q=0.3, */*;q=0.5, image/png;q=0.4", ["text/html", "image/png"] # image/png
|
||||
|
|
35
src/base_accept.cr
Normal file
35
src/base_accept.cr
Normal file
|
@ -0,0 +1,35 @@
|
|||
abstract struct Athena::Negotiation::BaseAccept
|
||||
getter quality : Float32 = 1.0
|
||||
getter normalized_value : String
|
||||
getter value : String
|
||||
getter parameters : Hash(String, String)
|
||||
getter type : String
|
||||
|
||||
def initialize(@value : String)
|
||||
# type, parameters = self.parse_accept_value value
|
||||
parts = @value.split ';'
|
||||
@type = parts.shift.strip.downcase
|
||||
|
||||
@parameters = parts.to_h do |part|
|
||||
part = part.split '='
|
||||
|
||||
# TODO: Use more specific exception
|
||||
raise ArgumentError.new "Invalid header: '#{@value}'." unless part.size == 2
|
||||
|
||||
{part[0].strip.downcase, part[1].strip(" \"")}
|
||||
end
|
||||
|
||||
if quality = @parameters.delete "q"
|
||||
@quality = quality.to_f32
|
||||
end
|
||||
|
||||
@normalized_value = String.build do |io|
|
||||
io << @type
|
||||
|
||||
unless @parameters.empty?
|
||||
io << "; "
|
||||
parameters.join(io, "; ") { |(k, v), io| io << "#{k}=#{v}" }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
7
src/negotiator.cr
Normal file
7
src/negotiator.cr
Normal file
|
@ -0,0 +1,7 @@
|
|||
require "./abstract_negotiator"
|
||||
|
||||
class Athena::Negotiation::Negotiator < Athena::Negotiation::AbstractNegotiator
|
||||
private def create_header(header : String) : ANG::BaseAccept
|
||||
ANG::Accept.new header
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue