Skip to content

fervagar/Error-Codes-Generator

Repository files navigation

Error Codes Generator

License: MIT

The Error Codes Generator defines structured module/submodule/error triplets in a YAML file and automatically generates a C header and string table. It can also emit JSON/CSV for downstream docs/tests, and builds a static library plus small utilities to inspect and use the codes directly.

Features

  • Manual or automatic ID assignment: modules and submodules may specify explicit IDs or be auto-allocated; error indices are assigned in declaration order per scope.
  • Binary-search lookup: ec_strerror() is O(log N) over a sorted table.
  • Deterministic encoding: identical YAML → identical numeric layout and header.
  • Configurable bit layout (with padding): from YAML via top-level configuration. If configuration is present, all fields must be provided: module, submodule, error_id, padding. Supported totals: 8, 16, 32.
  • Strict YAML validation: duplicate keys, overlapping IDs, invalid identifiers/widths are rejected.
  • Introspection enum: generator emits an enum with symbolic names and negative decimal constants for debugger-friendly casting.
  • JSON/CSV emission: optional --json and --csv outputs with stable schema for specs, docs, and tests.
  • Success block: optional success mapping to rename and describe the zero code (E_SUCCESS by default).
  • Reserved value: (error_code_t)-1 is unusable and excluded by EC_IS_VALID / EC_IS_ERROR.

Build

make

Steps:

  1. Parse error_codes.yml → Generate build/inc/error_codes.h and build/src/error_codes_str.c
  2. Compile and archive into build/lib/liberrorcodes.a
  3. Link:
    • build/bin/example
    • build/bin/dbg_print_all_errors
  4. Copy public artifacts to dist/
dist/
error_codes.h
liberrorcodes.a

Using in Your Project

gcc -Idist -Ldist your_app.c -o your_app -lerrorcodes

Example:

#include <stdio.h>
#include <inttypes.h>

#include "error_codes.h"

int main(void) {
    error_code_t ec = E_READ_DEV;
    printf("Error: 0x%04" PRIx16 " (%" PRId16 "): %s\n", (uint16_t)ec, ec, ec_strerror(ec));
    return 0;
}

YAML Schema

Top-level:

configuration:              # Optional; if present, all four fields are required
  module:     <int > 0>
  submodule:  <int > 0>
  error_id:   <int > 0>
  padding:    <int ≥ 0>     # MSB padding; increases total width, does not affect M|S|E

success:                    # Optional
  name: E_SUCCESS           # Default: E_SUCCESS
  description: "Success"    # Default: "Success"

modules:
  <ModuleName>:
    id: <int optional>
    errors:
      <ERROR_NAME>: "<Description>"
      ...
    submodules:
      <SubmoduleName>:
        id: <int optional, != 0>
        errors:
          <ERROR_NAME>: "<Description>"
          ...

Constraints:

  • If configuration is present, it must define all four fields; otherwise built-in defaults are used.
  • module + submodule + error_id + padding ∈ {8,16,32}.
  • Keys must be unique at every level (duplicate YAML keys are rejected).
  • Error names must match ^[A-Z][A-Z0-9_]*$.
  • Submodule ID 0 is reserved for module-level errors.
  • IDs must fit within their configured field width.

Bit Layout and Types

EC_BITS_* macros in the generated header:

EC_BITS_MODULE
EC_BITS_SUBMODULE
EC_BITS_ERROR
EC_BITS_PADDING
EC_BITS_TOTAL

ERROR_CODE_BITSIZE == EC_BITS_TOTAL, selecting:

  • 8 → int8_t
  • 16 → int16_t
  • 32 → int32_t

padding occupies the most significant bits and is ignored by the M|S|E grouping and extraction macros.

Examples:

configuration:
  module: 5
  submodule: 5
  error_id: 6
  padding: 0
# 32-bit layout
configuration:
  module: 8
  submodule: 8
  error_id: 16
  padding: 0

Manual vs Automatic ID Assignment

modules:
  Power:
    id: 1
    errors:
      E_START_FAIL: "Power failed to start"
      E_SHUTDOWN:   "Power failed to shut down"
    submodules:
      Sensor:
        id: 3
        errors:
          E_TIMEOUT: "Sensor timeout"
      Actuator:      # automatic; next available ID
        errors:
          E_RANGE:   "Actuator range exceeded"

Rules:

  • Explicit IDs preserved verbatim; missing IDs auto-assigned to the lowest unused integer (modules from 0; submodules from 1).
  • Error indices start at 0 in each (module, submodule) scope, in declaration order.
  • Violations (duplicates, out-of-range) are hard errors.

Success Block

success lets you rename or describe the zero code:

success:
  name: E_SUCCESS
  description: "Success"

If omitted: E_SUCCESS with description "Success" is generated.

Reserved Value

(error_code_t)-1 is reserved and never emitted. Macros:

EC_RESERVED_NEG1
EC_IS_VALID(code)   // true for 0 and all negative codes except -1
EC_IS_OK(code)      // code == 0
EC_IS_ERROR(code)   // negative codes except -1

Emitted Enum and API

The header exposes a debugger-friendly enum with negative decimal values:

enum ec_error_code_sym {
    E_SUCCESS = 0,
    /* ... */
    /* E_OPEN_DEV = -2048, etc. */
};

Public API:

typedef /* int8_t|int16_t|int32_t */ error_code_t;

struct error_desc {
    error_code_t code;
    const char *description;
};

const char *ec_strerror(error_code_t code);

extern const struct error_desc error_desc_array[];
extern const size_t error_desc_count;

Extraction and iteration helpers:

EC_EXTRACT_MODULE(code)
EC_EXTRACT_SUBMODULE(code)
EC_EXTRACT_ERROR(code)

EC_FOR_EACH_ERROR(ptr)
EC_FOR_EACH_ERROR_REVERSE(ptr)
EC_FOR_EACH_ERROR_IDX(i)
EC_FOR_EACH_ERROR_IDX_REVERSE(i)
EC_ERROR_AT(i)

CLI: Generator Outputs (C / JSON / CSV)

parser.py supports additional artifacts:

parser.py error_codes.yml \
  --header build/inc/error_codes.h \
  --cfile  build/src/error_codes_str.c \
  --json   build/error_codes.json \
  --csv    build/error_codes.csv
  • --header Path to generated header (C).
  • --cfile Path to generated string table (C).
  • --json Optional path; emits a UTF-8 JSON document with stable key order and a trailing newline.
  • --csv Optional path; emits CSV with stable columns.

JSON structure

{
  "configuration": {
    "module": 5,
    "submodule": 5,
    "error_id": 6,
    "padding": 0,
    "total": 16
  },
  "error_codes": [
    {
      "name": "E_OPEN_DEV",
      "code": -2048,
      "module": 1,
      "submodule": 0,
      "error": 0,
      "description": "Error opening device"
    }
  ]
}
  • Encoding: UTF-8 (ensure_ascii=false)
  • Pretty: indent=2
  • Stable: sort_keys=true
  • Strict numbers: allow_nan=false

CSV columns

name,code,module,submodule,error,description
E_OPEN_DEV,-2048,1,0,0,"Error opening device"

Example Output (dbg printer)

IDX  CODE(dec)    CODE(hex)  CODE(bin M|S|E)     MODULE   SUBMODULE  ERROR      DESCRIPTION
----------------------------------------------------------------------------------------------------
0    -2048        0xf800     00001|00000|000000  1        0          0          Error opening device
1    -2049        0xf7ff     00001|00000|000001  1        0          1          Error closing device
2    -2112        0xf7c0     00001|00001|000000  1        1          0          Error foo
3    -2113        0xf7bf     00001|00001|000001  1        1          1          Error bar
...

Cleaning

make clean       # remove build/
make distclean   # remove build/ and dist/

License

MIT — see LICENSE.

About

YAML-based C error code generator. Define and manage error codes effortlessly.

Resources

License

Stars

Watchers

Forks