sashite-qpi 1.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sashite/sin"
4
+ require "sashite/pin"
5
+
6
+ require_relative "constants"
7
+ require_relative "errors"
8
+
9
+ module Sashite
10
+ module Qpi
11
+ # Parser for QPI (Qualified Piece Identifier) strings.
12
+ #
13
+ # This parser splits the QPI string on the colon separator and delegates
14
+ # parsing of each component to the SIN and PIN libraries.
15
+ #
16
+ # @example
17
+ # Parser.parse("C:K") # => { sin: <Sin::Identifier>, pin: <Pin::Identifier> }
18
+ # Parser.parse("s:+r^") # => { sin: <Sin::Identifier>, pin: <Pin::Identifier> }
19
+ #
20
+ # @see https://sashite.dev/specs/qpi/1.0.0/
21
+ module Parser
22
+ # Parses a QPI string into its components.
23
+ #
24
+ # @param input [String] The QPI string to parse
25
+ # @return [Hash] A hash with :sin and :pin keys containing Identifier instances
26
+ # @raise [Errors::Argument] If the input is not a valid QPI string
27
+ def self.parse(input)
28
+ validate_input_type(input)
29
+ validate_not_empty(input)
30
+
31
+ sin_string, pin_string = split_components(input)
32
+
33
+ sin = parse_sin(sin_string)
34
+ pin = parse_pin(pin_string)
35
+
36
+ { sin: sin, pin: pin }
37
+ end
38
+
39
+ # Validates a QPI string without raising an exception.
40
+ #
41
+ # @param input [String] The QPI string to validate
42
+ # @return [Boolean] true if valid, false otherwise
43
+ def self.valid?(input)
44
+ return false unless ::String === input
45
+
46
+ parse(input)
47
+ true
48
+ rescue Errors::Argument
49
+ false
50
+ end
51
+
52
+ class << self
53
+ private
54
+
55
+ # Validates that input is a String.
56
+ #
57
+ # @param input [Object] The input to validate
58
+ # @raise [Errors::Argument] If input is not a String
59
+ def validate_input_type(input)
60
+ return if ::String === input
61
+
62
+ raise Errors::Argument, Errors::Argument::Messages::EMPTY_INPUT
63
+ end
64
+
65
+ # Validates that input is not empty.
66
+ #
67
+ # @param input [String] The input to validate
68
+ # @raise [Errors::Argument] If input is empty
69
+ def validate_not_empty(input)
70
+ return unless input.empty?
71
+
72
+ raise Errors::Argument, Errors::Argument::Messages::EMPTY_INPUT
73
+ end
74
+
75
+ # Splits the input into SIN and PIN components.
76
+ #
77
+ # @param input [String] The QPI string to split
78
+ # @return [Array<String>] An array with [sin_string, pin_string]
79
+ # @raise [Errors::Argument] If the separator is missing or components are empty
80
+ def split_components(input)
81
+ unless input.include?(Constants::SEPARATOR)
82
+ raise Errors::Argument, Errors::Argument::Messages::MISSING_SEPARATOR
83
+ end
84
+
85
+ sin_string, pin_string = input.split(Constants::SEPARATOR, 2)
86
+
87
+ if sin_string.nil? || sin_string.empty?
88
+ raise Errors::Argument, Errors::Argument::Messages::MISSING_SIN
89
+ end
90
+
91
+ if pin_string.nil? || pin_string.empty?
92
+ raise Errors::Argument, Errors::Argument::Messages::MISSING_PIN
93
+ end
94
+
95
+ [sin_string, pin_string]
96
+ end
97
+
98
+ # Parses the SIN component.
99
+ #
100
+ # @param sin_string [String] The SIN string to parse
101
+ # @return [Sashite::Sin::Identifier] The parsed SIN identifier
102
+ # @raise [Errors::Argument] If the SIN is invalid
103
+ def parse_sin(sin_string)
104
+ Sashite::Sin.parse(sin_string)
105
+ rescue ::ArgumentError => e
106
+ raise Errors::Argument, "#{Errors::Argument::Messages::INVALID_SIN}: #{e.message}"
107
+ end
108
+
109
+ # Parses the PIN component.
110
+ #
111
+ # @param pin_string [String] The PIN string to parse
112
+ # @return [Sashite::Pin::Identifier] The parsed PIN identifier
113
+ # @raise [Errors::Argument] If the PIN is invalid
114
+ def parse_pin(pin_string)
115
+ Sashite::Pin.parse(pin_string)
116
+ rescue ::ArgumentError => e
117
+ raise Errors::Argument, "#{Errors::Argument::Messages::INVALID_PIN}: #{e.message}"
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
data/lib/sashite/qpi.rb CHANGED
@@ -1,220 +1,92 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "qpi/constants"
4
+ require_relative "qpi/errors"
3
5
  require_relative "qpi/identifier"
6
+ require_relative "qpi/parser"
4
7
 
5
8
  module Sashite
6
- # QPI (Qualified Piece Identifier) implementation for Ruby
9
+ # QPI (Qualified Piece Identifier) implementation for Ruby.
7
10
  #
8
- # Provides a rule-agnostic format for identifying game pieces in abstract strategy board games
9
- # by combining Style Identifier Notation (SIN) and Piece Identifier Notation (PIN) primitives
10
- # with a colon separator. This combination enables complete piece identification across different
11
- # game styles and contexts.
11
+ # QPI provides complete piece identification by combining two primitive notations:
12
+ # - SIN (Style Identifier Notation) for Piece Style
13
+ # - PIN (Piece Identifier Notation) for Piece Name, Side, State, and Terminal Status
12
14
  #
13
- # ## Concept
15
+ # == Format
14
16
  #
15
- # QPI addresses the fundamental need to uniquely identify game pieces across different style
16
- # systems while maintaining complete attribute information. By combining SIN and PIN primitives,
17
- # QPI provides explicit representation of all four fundamental piece attributes from the
18
- # Sashité Protocol.
17
+ # <sin>:<pin>
19
18
  #
20
- # ## Four Fundamental Attributes
19
+ # - *SIN*: Single ASCII letter encoding Piece Style and a side tag
20
+ # - *PIN*: Piece identifier with optional state modifier and terminal marker
21
21
  #
22
- # QPI represents all four piece attributes through primitive combination:
23
- # - **Family**: Style identification from SIN component
24
- # - **Type**: Piece type from PIN component
25
- # - **Side**: Player assignment from both components (must be consistent)
26
- # - **State**: Piece state from PIN component
22
+ # == Piece Identity Attributes
27
23
  #
28
- # ## Format Structure
24
+ # A QPI token encodes complete Piece Identity:
29
25
  #
30
- # A QPI identifier consists of two primitive components separated by a colon:
31
- # - **SIN component**: Style identification with player assignment
32
- # - **PIN component**: Piece identification with type, side, and state
33
- # - **Separator**: Colon (:) provides clear delimitation
26
+ # - *Piece Style* SIN letter (case-insensitive identity)
27
+ # - *Piece Name* PIN letter (case-insensitive identity)
28
+ # - *Piece Side* → PIN letter case (uppercase = first, lowercase = second)
29
+ # - *Piece State* PIN modifier (+/-)
30
+ # - *Terminal Status* → PIN marker (^)
34
31
  #
35
- # The components must maintain semantic consistency: both SIN and PIN must represent
36
- # the same player (first or second) through their respective case encodings.
32
+ # == Native/Derived Relationship
37
33
  #
38
- # ## Semantic Consistency Constraint
34
+ # QPI defines a deterministic relationship based on case comparison:
39
35
  #
40
- # QPI enforces a critical constraint: the style identified by the SIN component must be
41
- # associated with the same player as indicated by the PIN component. This ensures that
42
- # piece ownership and style ownership remain aligned, preventing impossible combinations
43
- # like a first player style with a second player piece.
36
+ # - *Native*: SIN case matches PIN case (sin.side == pin.side)
37
+ # - *Derived*: SIN case differs from PIN case (sin.side != pin.side)
44
38
  #
45
- # Examples of semantic consistency:
46
- # - SIN "C" (first player) + PIN "K" (first player) = Valid
47
- # - SIN "c" (second player) + PIN "k" (second player) = Valid
48
- # - SIN "C" (first player) + PIN "k" (second player) = Invalid
49
- # - SIN "c" (second player) + PIN "K" (first player) = Invalid
39
+ # == Examples
50
40
  #
51
- # ## Cross-Style Gaming Support
41
+ # qpi = Sashite::Qpi.parse("C:K^")
42
+ # qpi.sin.style # => :C
43
+ # qpi.pin.type # => :K
44
+ # qpi.pin.side # => :first
45
+ # qpi.pin.state # => :normal
46
+ # qpi.pin.terminal? # => true
47
+ # qpi.native? # => true
52
48
  #
53
- # QPI enables cross-style gaming scenarios where different players use different game
54
- # traditions. The explicit style identification allows pieces from different systems
55
- # to coexist while maintaining clear attribution to their respective players.
49
+ # qpi = Sashite::Qpi.parse("C:k")
50
+ # qpi.derived? # => true
51
+ # qpi.native.to_s # => "C:K"
56
52
  #
57
- # ## Format Specification
53
+ # Sashite::Qpi.valid?("C:K^") # => true
54
+ # Sashite::Qpi.valid?("invalid") # => false
58
55
  #
59
- # Structure: `<sin>:<pin>`
60
- #
61
- # Grammar (BNF):
62
- # <qpi> ::= <uppercase-qpi> | <lowercase-qpi>
63
- # <uppercase-qpi> ::= <uppercase-letter> ":" <uppercase-pin>
64
- # <lowercase-qpi> ::= <lowercase-letter> ":" <lowercase-pin>
65
- # <uppercase-pin> ::= ["+" | "-"] <uppercase-letter>
66
- # <lowercase-pin> ::= ["+" | "-"] <lowercase-letter>
67
- #
68
- # Regular Expression: `/\A([A-Z]:[-+]?[A-Z]|[a-z]:[-+]?[a-z])\z/`
69
- #
70
- # ## Attribute Mapping
71
- #
72
- # QPI encodes piece attributes through primitive combination:
73
- #
74
- # | Piece Attribute | QPI Encoding | Examples |
75
- # |-----------------|--------------|----------|
76
- # | **Family** | SIN component | `C:K` = Chess family, `O:K` = Ogi family |
77
- # | **Type** | PIN letter choice | `C:K` = King, `C:P` = Pawn |
78
- # | **Side** | Component cases | `C:K` = First player, `c:k` = Second player |
79
- # | **State** | PIN prefix modifier | `O:+P` = Enhanced, `C:-P` = Diminished |
80
- #
81
- # ## System Constraints
82
- #
83
- # - **Semantic Consistency**: SIN and PIN components must represent the same player
84
- # - **Component Validation**: Each component must be valid according to its specification
85
- # - **Complete Attribution**: All four fundamental piece attributes explicitly represented
86
- # - **Cross-Style Support**: Enables multi-tradition gaming environments
87
- #
88
- # ## Examples
89
- #
90
- # ### Single-Style Games
91
- #
92
- # # Chess (both players use Chess style)
93
- # white_king = Sashite::Qpi.parse("C:K") # Chess king, first player
94
- # black_king = Sashite::Qpi.parse("c:k") # Chess king, second player
95
- #
96
- # # Ogi (both players use Ogi style)
97
- # sente_king = Sashite::Qpi.parse("O:K") # Ogi king, first player (sente)
98
- # gote_rook = Sashite::Qpi.parse("o:+r") # Ogi promoted rook, second player (gote)
99
- #
100
- # ### Cross-Style Games
101
- #
102
- # # Chess vs. Ogi match
103
- # chess_player = Sashite::Qpi.parse("C:K") # First player uses Chess
104
- # ogi_player = Sashite::Qpi.parse("o:k") # Second player uses Ogi
105
- #
106
- # # Verify cross-style scenario
107
- # chess_player.cross_family?(ogi_player) # => true
108
- #
109
- # ### Attribute Access and Manipulation
110
- #
111
- # identifier = Sashite::Qpi.parse("O:+R")
112
- #
113
- # # Four fundamental attributes
114
- # identifier.family # => :O
115
- # identifier.type # => :R
116
- # identifier.side # => :first
117
- # identifier.state # => :enhanced
118
- #
119
- # # Component extraction
120
- # identifier.to_sin # => "O"
121
- # identifier.to_pin # => "+R"
122
- #
123
- # # Immutable transformations
124
- # flipped = identifier.flip # => "o:+r"
125
- # different_type = identifier.with_type(:Q) # => "O:+Q"
126
- # different_family = identifier.with_family(:C) # => "C:+R"
127
- #
128
- # ## Design Properties
129
- #
130
- # - **Rule-agnostic**: Independent of specific game mechanics
131
- # - **Complete identification**: Explicit representation of all four piece attributes
132
- # - **Cross-style support**: Enables multi-tradition gaming environments
133
- # - **Semantic validation**: Ensures consistency between style and piece ownership
134
- # - **Primitive foundation**: Built from foundational SIN and PIN building blocks
135
- # - **Extension-ready**: Can be enhanced by human-readable naming systems
136
- # - **Context-flexible**: Adaptable to various identification needs
137
- # - **Immutable**: All instances are frozen and transformations return new objects
138
- # - **Functional**: Pure functions with no side effects
139
- #
140
- # @see https://sashite.dev/specs/qpi/1.0.0/ QPI Specification v1.0.0
141
- # @see https://sashite.dev/specs/qpi/1.0.0/examples/ QPI Examples
142
- # @see https://sashite.dev/specs/sin/1.0.0/ Style Identifier Notation (SIN)
143
- # @see https://sashite.dev/specs/pin/1.0.0/ Piece Identifier Notation (PIN)
56
+ # @see https://sashite.dev/specs/qpi/1.0.0/
144
57
  module Qpi
145
- # Check if a string is a valid QPI notation
58
+ # Parses a QPI string into an Identifier.
146
59
  #
147
- # Validates the string format and semantic consistency between SIN and PIN components.
148
- # Both components must be individually valid and represent the same player through
149
- # their respective case encodings.
60
+ # @param string [String] The QPI string to parse
61
+ # @return [Identifier] A new Identifier instance
62
+ # @raise [Errors::Argument] If the string is not a valid QPI
150
63
  #
151
- # @param qpi_string [String] the string to validate
152
- # @return [Boolean] true if valid QPI, false otherwise
64
+ # @example
65
+ # Sashite::Qpi.parse("C:K")
66
+ # # => #<Sashite::Qpi::Identifier C:K>
153
67
  #
154
- # @example Validate various QPI formats
155
- # Sashite::Qpi.valid?("C:K") # => true (Chess king, first player)
156
- # Sashite::Qpi.valid?("c:k") # => true (Chess king, second player)
157
- # Sashite::Qpi.valid?("O:+P") # => true (Ogi enhanced pawn, first player)
158
- # Sashite::Qpi.valid?("o:-r") # => true (Ogi diminished rook, second player)
159
- # Sashite::Qpi.valid?("C:k") # => false (semantic mismatch: first player style, second player piece)
160
- # Sashite::Qpi.valid?("c:K") # => false (semantic mismatch: second player style, first player piece)
161
- # Sashite::Qpi.valid?("CHESS:K") # => false (multi-character SIN component)
162
- # Sashite::Qpi.valid?("C") # => false (missing PIN component)
163
- def self.valid?(qpi_string)
164
- Identifier.valid?(qpi_string)
165
- end
166
-
167
- # Parse a QPI string into an Identifier object
68
+ # Sashite::Qpi.parse("s:+r^")
69
+ # # => #<Sashite::Qpi::Identifier s:+r^>
168
70
  #
169
- # Creates a new QPI identifier by parsing the string into SIN and PIN components,
170
- # validating each component, and ensuring semantic consistency between them.
171
- #
172
- # @param qpi_string [String] QPI notation string (format: sin:pin)
173
- # @return [Qpi::Identifier] parsed identifier object with family, type, side, and state attributes
174
- # @raise [ArgumentError] if the QPI string is invalid or semantically inconsistent
175
- #
176
- # @example Parse different QPI formats with complete attribute access
177
- # Sashite::Qpi.parse("C:K") # => #<Qpi::Identifier family=:C type=:K side=:first state=:normal>
178
- # Sashite::Qpi.parse("c:k") # => #<Qpi::Identifier family=:C type=:K side=:second state=:normal>
179
- # Sashite::Qpi.parse("O:+R") # => #<Qpi::Identifier family=:O type=:R side=:first state=:enhanced>
180
- # Sashite::Qpi.parse("x:-s") # => #<Qpi::Identifier family=:X type=:S side=:second state=:diminished>
181
- #
182
- # @example Traditional game styles
183
- # chess_king = Sashite::Qpi.parse("C:K") # Chess king, first player
184
- # ogi_rook = Sashite::Qpi.parse("o:+r") # Ogi promoted rook, second player
185
- # xiongqi_king = Sashite::Qpi.parse("X:K") # Xiongqi king, first player
186
- def self.parse(qpi_string)
187
- Identifier.parse(qpi_string)
71
+ # Sashite::Qpi.parse("invalid")
72
+ # # => raises Errors::Argument
73
+ def self.parse(string)
74
+ components = Parser.parse(string)
75
+
76
+ Identifier.new(components[:sin], components[:pin])
188
77
  end
189
78
 
190
- # Create a new identifier instance with explicit parameters
191
- #
192
- # Constructs a QPI identifier by directly specifying all four fundamental attributes.
193
- # This method provides parameter-based construction as an alternative to string parsing,
194
- # enabling immediate validation and clearer API usage.
195
- #
196
- # @param family [Symbol] style family identifier (single ASCII letter as symbol)
197
- # @param type [Symbol] piece type (:A to :Z)
198
- # @param side [Symbol] player side (:first or :second)
199
- # @param state [Symbol] piece state (:normal, :enhanced, or :diminished)
200
- # @return [Qpi::Identifier] new immutable identifier instance
201
- # @raise [ArgumentError] if parameters are invalid or semantically inconsistent
202
- #
203
- # @example Create identifiers with explicit parameters
204
- # Sashite::Qpi.identifier(:C, :K, :first, :normal) # => "C:K"
205
- # Sashite::Qpi.identifier(:c, :K, :second, :normal) # => "c:k"
206
- # Sashite::Qpi.identifier(:O, :R, :first, :enhanced) # => "O:+R"
207
- # Sashite::Qpi.identifier(:x, :S, :second, :diminished) # => "x:-s"
79
+ # Checks if a string is a valid QPI notation.
208
80
  #
209
- # @example Cross-style game setup
210
- # chess_player = Sashite::Qpi.identifier(:C, :K, :first, :normal) # Chess king, first player
211
- # ogi_player = Sashite::Qpi.identifier(:o, :K, :second, :normal) # Ogi king, second player
81
+ # @param string [String] The string to validate
82
+ # @return [Boolean] true if valid, false otherwise
212
83
  #
213
- # chess_player.cross_family?(ogi_player) # => true (different families)
214
- # chess_player.same_type?(ogi_player) # => true (both kings)
215
- # chess_player.same_side?(ogi_player) # => false (different players)
216
- def self.identifier(family, type, side, state = Pin::Identifier::NORMAL_STATE)
217
- Identifier.new(family, type, side, state)
84
+ # @example
85
+ # Sashite::Qpi.valid?("C:K") # => true
86
+ # Sashite::Qpi.valid?("s:+r^") # => true
87
+ # Sashite::Qpi.valid?("invalid") # => false
88
+ def self.valid?(string)
89
+ Parser.valid?(string)
218
90
  end
219
91
  end
220
92
  end
data/lib/sashite-qpi.rb CHANGED
@@ -1,14 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "sashite/qpi"
4
-
5
- # Sashité namespace for board game notation libraries
6
- #
7
- # Sashité provides a collection of libraries for representing and manipulating
8
- # board game concepts according to the Game Protocol specifications.
9
- #
10
- # @see https://sashite.dev/game-protocol/ Game Protocol Foundation
11
- # @see https://sashite.dev/specs/ Sashité Specifications
12
- # @author Sashité
13
- module Sashite
14
- end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sashite-qpi
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyril Kato
@@ -15,56 +15,57 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: '3.1'
18
+ version: 4.0.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: '3.1'
25
+ version: 4.0.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: sashite-sin
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: '2.1'
32
+ version: 3.0.0
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '2.1'
40
- description: |
41
- QPI (Qualified Piece Identifier) provides a rule-agnostic format for identifying game pieces
42
- in abstract strategy board games by combining Style Identifier Notation (SIN) and Piece
43
- Identifier Notation (PIN) primitives. This gem implements the QPI Specification v1.0.0 with
44
- a modern Ruby interface featuring immutable identifier objects and functional programming
45
- principles. QPI enables complete piece identification with all four fundamental attributes
46
- (family, type, side, state) while supporting cross-style gaming environments. Perfect for
47
- multi-tradition board games, hybrid gaming systems, and game engines requiring comprehensive
48
- piece identification across different game styles and traditions.
39
+ version: 3.0.0
40
+ description: QPI (Qualified Piece Identifier) implementation for Ruby. Provides a
41
+ rule-agnostic format for complete piece identification in abstract strategy board
42
+ games by combining SIN and PIN primitives, with Native/Derived relationship support.
49
43
  email: contact@cyril.email
50
44
  executables: []
51
45
  extensions: []
52
46
  extra_rdoc_files: []
53
47
  files:
54
- - LICENSE.md
48
+ - LICENSE
55
49
  - README.md
56
50
  - lib/sashite-qpi.rb
57
51
  - lib/sashite/qpi.rb
52
+ - lib/sashite/qpi/constants.rb
53
+ - lib/sashite/qpi/errors.rb
54
+ - lib/sashite/qpi/errors/argument.rb
55
+ - lib/sashite/qpi/errors/argument/messages.rb
58
56
  - lib/sashite/qpi/identifier.rb
57
+ - lib/sashite/qpi/parser.rb
59
58
  homepage: https://github.com/sashite/qpi.rb
60
59
  licenses:
61
- - MIT
60
+ - Apache-2.0
62
61
  metadata:
63
62
  bug_tracker_uri: https://github.com/sashite/qpi.rb/issues
64
63
  documentation_uri: https://rubydoc.info/github/sashite/qpi.rb/main
65
64
  homepage_uri: https://github.com/sashite/qpi.rb
66
65
  source_code_uri: https://github.com/sashite/qpi.rb
67
66
  specification_uri: https://sashite.dev/specs/qpi/1.0.0/
67
+ wiki_uri: https://sashite.dev/specs/qpi/1.0.0/examples/
68
+ funding_uri: https://github.com/sponsors/sashite
68
69
  rubygems_mfa_required: 'true'
69
70
  rdoc_options: []
70
71
  require_paths:
@@ -80,7 +81,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
80
81
  - !ruby/object:Gem::Version
81
82
  version: '0'
82
83
  requirements: []
83
- rubygems_version: 3.7.1
84
+ rubygems_version: 4.0.3
84
85
  specification_version: 4
85
86
  summary: QPI (Qualified Piece Identifier) implementation for Ruby with immutable identifier
86
87
  objects
data/LICENSE.md DELETED
@@ -1,22 +0,0 @@
1
- Copyright (c) 2014-2025 Sashite
2
-
3
- MIT License
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- "Software"), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
11
- the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be
14
- included in all copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.