Mapping pipeline¶
The three-namespace problem¶
IBM MQ uses multiple naming conventions depending on the interface:
- MQSC names (e.g.
CURDEPTH,DEFPSIST) - Short, uppercase tokens used in MQSC commands and the REST API's
runCommandJSONendpoint. - PCF names (e.g.
CurrentQDepth,DefPersistence) - CamelCase names from the Programmable Command Formats. Not used
directly by
mq-rest-admin, but they form the intermediate namespace in the mapping pipeline. - Developer names (e.g.
current_queue_depth,default_persistence) - Human-readable
snake_casenames for use in application code.
The mapping pipeline translates between MQSC and developer names. PCF names were used as an intermediate reference during the original extraction process that bootstrapped the mapping tables but do not appear at runtime.
// With mapping enabled (default) — developer names
let queues = session.display_queue(
Some("MY.QUEUE"),
None,
Some(vec!["current_queue_depth".into(), "max_queue_depth".into()]),
None,
)?;
// Returns: [{"queue_name": "MY.QUEUE", "current_queue_depth": 0, "max_queue_depth": 5000}]
Qualifier-based mapping¶
Mappings are organized by qualifier (e.g. queue, channel, qmgr),
not by command. A single qualifier's mapping tables serve all commands
that operate on that object type. For example, the queue qualifier
covers DISPLAY QUEUE, DEFINE QLOCAL, DELETE QALIAS, and all other
queue-related commands.
This design avoids duplicating mapping data across commands and reflects how MQSC attributes are shared across command verbs.
See qualifier mapping reference for the complete per-qualifier reference.
Request mapping flow¶
When mapping is enabled, request attributes are translated before sending to the MQ REST API:
-
Key mapping: Each
snake_caseattribute name is looked up in the qualifier'srequest_key_map. If found, the key is replaced with the MQSC parameter name. -
Value mapping: For attributes with enumerated values, the qualifier's
request_value_maptranslates Rust values to MQSC values (e.g."yes"→"YES"). -
Key-value mapping: Some attributes require both key and value to change simultaneously. The
request_key_value_maphandles cases where a single Rust attribute expands to a different MQSC key+value pair (e.g.channel_type="server_connection"→CHLTYPE("SVRCONN")).
use std::collections::HashMap;
let mut params = HashMap::new();
params.insert("max_queue_depth".into(), serde_json::json!(50000));
params.insert("default_persistence".into(), serde_json::json!("yes"));
params.insert("description".into(), serde_json::json!("Application queue"));
session.define_qlocal(
Some("MY.QUEUE"),
Some(params),
None,
None,
)?;
// After request mapping, the JSON payload sent to MQ contains:
// { "MAXDEPTH": 50000, "DEFPSIST": "YES", "DESCR": "Application queue" }
Response mapping flow¶
Response attributes are translated after receiving the MQ REST response:
-
Key mapping: Each MQSC parameter name from the response is looked up in the qualifier's
response_key_map. If found, the key is replaced with thesnake_casename. -
Value mapping: Enumerated MQSC values are translated to Rust-friendly values via the
response_value_map(e.g."YES"→"yes").
let queues = session.display_queue(
Some("MY.QUEUE"),
None,
Some(vec!["current_queue_depth".into(), "max_queue_depth".into()]),
None,
)?;
// MQ returns MQSC names: {"queue": "MY.QUEUE", "curdepth": 0, "maxdepth": 5000}
// After response mapping: {"queue_name": "MY.QUEUE", "current_queue_depth": 0, "max_queue_depth": 5000}
Response parameter mapping¶
When the caller specifies response_parameters (the list of attributes
to return), those names are also mapped from snake_case to MQSC before
being sent in the request. This allows callers to request specific
attributes using Rust names.
Response parameter macros (like CFCONLOS for channel status) are
recognized and passed through without mapping.
WHERE keyword mapping¶
The where_clause parameter on DISPLAY methods accepts a filter expression
like "current_queue_depth GT 100". The first token (the keyword) is mapped
from snake_case to the MQSC name. The rest of the expression is
passed through unchanged.
Strict vs lenient mode¶
Strict mode (default): Any attribute name or value that cannot be
mapped returns a MqRestError::Mapping error. This catches typos and
unsupported attributes early.
Lenient mode (.mapping_strict(false)): Unknown attribute names and
values pass through unchanged. This is useful when working with
attributes not yet covered by the mapping tables.
The mode is set at session creation and applies to all mapping operations. It cannot be overridden per-call.
// Strict mode (default) — unknown attributes return MqRestError::Mapping
let session = MqRestSession::builder()
.rest_base_url("https://localhost:9443/ibmmq/rest/v2")
.qmgr_name("QM1")
.credentials(Credentials::Ltpa {
username: "mqadmin".into(),
password: "mqadmin".into(),
})
.mapping_strict(true)
.build()?;
// Lenient mode — unknown attributes pass through unchanged
let session = MqRestSession::builder()
.rest_base_url("https://localhost:9443/ibmmq/rest/v2")
.qmgr_name("QM1")
.credentials(Credentials::Ltpa {
username: "mqadmin".into(),
password: "mqadmin".into(),
})
.mapping_strict(false)
.build()?;
Qualifier resolution¶
When a command is executed, the mapping qualifier is resolved by:
- Looking up the command key (e.g.
"DISPLAY QUEUE") in the command metadata for an explicit qualifier. - Falling back to a hardcoded default map (e.g.
QLOCAL→queue,CHANNEL→channel). - As a last resort, lowercasing the MQSC qualifier.
This means DEFINE QLOCAL, DEFINE QREMOTE, and DISPLAY QUEUE all
resolve to the queue qualifier and share the same mapping tables.
Custom mapping overrides¶
The built-in mapping tables cover all standard MQSC attributes, but sites may
use different snake_case conventions. The mapping_overrides option on
MqRestSessionBuilder lets you layer sparse changes on top of the built-in
data without replacing it.
How merging works¶
Overrides are merged at the key level within each sub-map:
use serde_json::json;
let overrides = json!({
"qualifiers": {
"queue": {
"response_key_map": {
"CURDEPTH": "queue_depth" // replaces the built-in mapping for CURDEPTH
}
}
}
});
When this override is applied:
- The built-in mapping data is deep-copied (the original is never mutated).
- The
queuequalifier'sresponse_key_mapis updated: the entry forCURDEPTHchanges from"current_queue_depth"to"queue_depth". - All other entries in
response_key_map(and all other sub-maps) remain unchanged.
This means you only specify the entries you want to change. A single override entry doesn't affect the hundreds of other mappings.
Supported override keys¶
The top level of the overrides object accepts two keys:
commands: Override command-level metadata (e.g. which qualifier a command resolves to). Each command entry is shallow-merged.qualifiers: Override qualifier mapping tables. Each qualifier supports five sub-maps:request_key_map—snake_case→ MQSC key mapping for requestsrequest_value_map— value translations for request attributesrequest_key_value_map— combined key+value translations for requestsresponse_key_map— MQSC →snake_casekey mapping for responsesresponse_value_map— value translations for response attributes
Adding new qualifiers¶
You can add mappings for qualifiers not yet covered by the built-in data:
use serde_json::json;
let overrides = json!({
"qualifiers": {
"custom_object": {
"request_key_map": {"my_attr": "MYATTR"},
"response_key_map": {"MYATTR": "my_attr"},
"request_value_map": {},
"response_value_map": {}
}
}
});
Validation¶
The override structure is validated at session construction time. Invalid shapes return errors immediately, so problems are caught before any commands are sent.
Opting out¶
Mapping is controlled at the session level. Pass .map_attributes(false)
when building the session to disable mapping entirely:
let session = MqRestSession::builder()
.rest_base_url("https://localhost:9443/ibmmq/rest/v2")
.qmgr_name("QM1")
.credentials(Credentials::Ltpa {
username: "mqadmin".into(),
password: "mqadmin".into(),
})
.map_attributes(false)
.build()?;
When mapping is disabled, attributes pass through in their native MQSC form.