diff --git a/spec/spectator/core/location_spec.cr b/spec/spectator/core/location_spec.cr new file mode 100644 index 0000000..57806d5 --- /dev/null +++ b/spec/spectator/core/location_spec.cr @@ -0,0 +1,41 @@ +require "../../spec_helper" + +describe Spectator::Core::Location do + describe ".parse" do + it "parses a location" do + location = Spectator::Core::Location.parse("foo:10") + location.file.should eq("foo") + location.line?.should eq(10) + end + + it "parses a location with no line number" do + location = Spectator::Core::Location.parse("foo") + location.file.should eq("foo") + location.line?.should be_nil + end + + it "parses a Windows path with a colon" do + location = Spectator::Core::Location.parse("C:\\foo:10") + location.file.should eq("C:\\foo") + location.line?.should eq(10) + end + + it "parses a Windows path with no line number" do + location = Spectator::Core::Location.parse("C:\\foo") + location.file.should eq("C:\\foo") + location.line?.should be_nil + end + end + + describe "#to_s" do + it "constructs a string representation" do + location = Spectator::Core::Location.new("foo", 10) + location.to_s.should eq("foo:10") + end + + it "constructs a string representation with no line number" do + location = Spectator::Core::Location.new("foo") + location.to_s.should eq("foo") + end + end +end diff --git a/src/spectator/core/location.cr b/src/spectator/core/location.cr new file mode 100644 index 0000000..8582a84 --- /dev/null +++ b/src/spectator/core/location.cr @@ -0,0 +1,34 @@ +module Spectator::Core + # Information about a location in the source code. + struct Location + # Name of the source file. + getter file : String + + # Line number in the source file. + getter! line : Int32 + + # Creates a new location. + def initialize(@file, @line = nil) + end + + # Parses a string into a location. + def self.parse(string : String) : self + # Avoid issues on Windows with colons in filenames (drive letters). + colon_index = string.rindex(':') + return new(string) unless colon_index + file = string[0...colon_index] + line = string[colon_index + 1, string.size - colon_index - 1].to_i? + # Use the whole string if the line number can't be parsed. + # This likely means it is a Windows path with a colon and no line number. + line ? new(file, line) : new(string) + end + + # Constructs a string representation of the location. + def to_s(io : IO) : Nil + io << @file + if line = @line + io << ':' << line + end + end + end +end