1 # A Locator is used to parse and manipulate Keep locator strings.
3 # Locators obey the following syntax:
5 # locator ::= address hint*
6 # address ::= digest size-hint
7 # digest ::= <32 hexadecimal digits>
8 # size-hint ::= "+" [0-9]+
9 # hint ::= "+" hint-type hint-content
11 # hint-content ::= [A-Za-z0-9@_-]+
13 # Individual hints may have their own required format:
15 # sign-hint ::= "+A" <40 lowercase hex digits> "@" sign-timestamp
16 # sign-timestamp ::= <8 lowercase hex digits>
19 def initialize(hasharg, sizearg, hintarg)
25 # Locator.parse returns a Locator object parsed from the string tok.
26 # Returns nil if tok could not be parsed as a valid locator.
30 rescue ArgumentError => e
35 # Locator.parse! returns a Locator object parsed from the string tok,
36 # raising an ArgumentError if tok cannot be parsed.
38 if tok.nil? or tok.empty?
39 raise ArgumentError.new "locator is nil or empty"
42 m = /^([[:xdigit:]]{32})(\+([[:digit:]]+))?(\+([[:upper:]][[:alnum:]+@_-]*))?$/.match(tok.strip)
44 raise ArgumentError.new "not a valid locator #{tok}"
47 raise ArgumentError.new "missing size hint on #{tok}"
50 tokhash, _, toksize, _, trailer = m[1..5]
53 trailer.split('+').each do |hint|
54 if hint =~ /^[[:upper:]][[:alnum:]@_-]+$/
57 raise ArgumentError.new "unknown hint #{hint}"
62 Locator.new(tokhash, toksize, tokhints)
65 # Returns the signature hint supplied with this locator,
66 # or nil if the locator was not signed.
68 @hints.grep(/^A/).first
71 # Returns an unsigned Locator.
73 Locator.new(@hash, @size, @hints.reject { |o| o.start_with?("A") })
77 Locator.new(@hash, @size, [])
98 [ @hash, @size, *@hints ].join('+')