openapi: 3.0.3
info:
  title: qr-svg
  version: 1.0.0
  description: >-
    Генератор QR-кодов в SVG, реализованный с нуля: режимы numeric, alphanumeric, byte (UTF-8), версии 1-40,
    уровни коррекции L, M, Q, H, Рид-Соломон над GF(256), выбор маски по штрафным очкам. Демо-проект.
  contact:
    name: Ewsir
    url: https://github.com/ewsir247
servers:
  - url: http://localhost:8121
paths:
  /healthz:
    get:
      summary: Проверка живости
      responses:
        '200':
          description: Сервис работает
          content:
            application/json:
              schema: { type: object, properties: { status: { type: string, example: ok } } }
  /v1/qr.svg:
    get:
      summary: QR-код в SVG
      description: Цвета задаются hex без решётки (000, 1a2b3c, 1a2b3c80) или с ней в виде %23; фон допускает transparent.
      parameters:
        - { in: query, name: text, required: true, schema: { type: string, maxLength: 2000 } }
        - { in: query, name: ecc, schema: { type: string, enum: [L, M, Q, H], default: M } }
        - {
            in: query,
            name: margin,
            schema: { type: integer, minimum: 0, maximum: 20, default: 4 },
            description: Тихая зона в модулях,
          }
        - { in: query, name: fg, schema: { type: string, default: '000000' } }
        - { in: query, name: bg, schema: { type: string, default: 'ffffff' } }
        - {
            in: query,
            name: size,
            schema: { type: integer, minimum: 16, maximum: 4096 },
            description: Ширина и высота в пикселях (без неё SVG масштабируется),
          }
        - {
            in: query,
            name: mode,
            schema: { type: string, enum: [numeric, alphanumeric, byte] },
            description: По умолчанию определяется по тексту,
          }
        - {
            in: query,
            name: mask,
            schema: { type: integer, minimum: 0, maximum: 7 },
            description: По умолчанию выбирается с наименьшим штрафом,
          }
        - { in: query, name: min_version, schema: { type: integer, minimum: 1, maximum: 40 } }
      responses:
        '200':
          description: SVG
          content:
            image/svg+xml:
              schema: { type: string }
        '422': { $ref: '#/components/responses/Error' }
        '429': { $ref: '#/components/responses/Error' }
  /v1/qr:
    post:
      summary: QR-код из JSON
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [text]
              properties:
                text: { type: string, maxLength: 2000 }
                ecc: { type: string, enum: [L, M, Q, H], default: M }
                margin: { type: integer, minimum: 0, maximum: 20, default: 4 }
                fg: { type: string, default: '#000000' }
                bg: { type: string, default: '#ffffff' }
                size: { type: integer, minimum: 16, maximum: 4096 }
                mode: { type: string, enum: [numeric, alphanumeric, byte] }
                mask: { type: integer, minimum: 0, maximum: 7 }
                min_version: { type: integer, minimum: 1, maximum: 40 }
                format: { type: string, enum: [svg, json], default: svg }
      responses:
        '200':
          description: SVG либо JSON с матрицей и контрастом
          content:
            image/svg+xml:
              schema: { type: string }
            application/json:
              schema:
                type: object
                properties:
                  svg: { type: string }
                  version: { type: integer }
                  size: { type: integer }
                  mode: { type: string }
                  ecc: { type: string }
                  mask: { type: integer }
                  modules:
                    {
                      type: array,
                      items: { type: string },
                      description: Строки из 0 и 1,
                      1 это тёмный модуль,
                    }
                  contrast:
                    type: object
                    properties:
                      ratio: { type: number }
                      aa_normal_text: { type: boolean }
                      aa_large_and_graphics: { type: boolean }
                      inverted: { type: boolean }
                      scannable: { type: boolean }
                      note: { type: string }
        '413': { $ref: '#/components/responses/Error' }
        '415': { $ref: '#/components/responses/Error' }
        '422': { $ref: '#/components/responses/Error' }
        '429': { $ref: '#/components/responses/Error' }
components:
  responses:
    Error:
      description: Ошибка (text_too_long, too_long, validation_error, rate_limited, ...)
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                type: object
                properties:
                  code: { type: string }
                  message: { type: string }
