Sign in

    moon_ua_parser_middleware_core

    Shared pure helper for framework middleware adapters of vicoproplus/moon_ua_parser: UaInfo assembly, degradation judgment and forensics record construction

    user-agent
    middleware
    ua-parser
    http
    Download zip
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    last month
    Downloads
    22

    #moon_ua_parser_middleware_core

    Shared pure helper for framework middleware adapters of vicoproplus/moon_ua_parser.

    No framework API, no IO — only pure functions to:

    • assemble the library's parse result (Result[UaInfo, UaError]) into the effective UaInfo a middleware should mount (assemble);
    • judge degradation: Err results, or parses whose three domain families all equal the library's all-miss fallback (FALLBACK_FAMILY, verified against the vendored uap-core snapshot), are degraded (degradation_reason / is_degraded);
    • construct forensics records (ForensicsRecord): the UA truncated to at most 64 characters plus a machine-readable reason (make_forensics).

    Degraded requests mount empty_ua_info() (empty-string families, None versions — distinct from the library's "Other" fallback) and the request chain continues without surfacing an error. The framework thin layer only forwards the forensics record to its own log channel.

    ForensicsRecord

    pub(all) struct ForensicsRecord {
    ua_summary : String
    reason : String
    } derive(Eq,
    Debug
    )

    A forensics record describing one degraded parse. Built only by this module (make_forensics); the framework thin layer forwards it to its log channel (PS-2) — this module performs no IO itself.

    MiddlewareOutcome

    pub(all) struct MiddlewareOutcome {
    ua_info :
    UaInfo

    degraded : Bool
    forensics : ForensicsRecord?
    }

    Outcome of assemble: what the middleware mounts for this request, plus the optional forensics record to log.

    No derive: the embedded @ua_parser.UaInfo implements neither Debug nor a non-deprecated Show (the lib derives the deprecated Show, engine baseline), so this struct is compared/asserted field-by-field (its ua_info field supports == via UaInfo's Eq).

    FALLBACK_FAMILY

    let FALLBACK_FAMILY : String

    The family literal the library reports for a domain whose rules all missed (the "empty fallback"). Verified against the current snapshot — do not "fix" from memory:

    • Library source: moon_ua_parser_lib/src/ua_parser/engine.mbt:33-65 (other_browser / other_os / other_device all emit family: "Other" with every other field None).
    • uap-core goldens: browser uap-core/tests/test_ua.yaml:1020-1024 (SomethingWeNeverKnewExisted -> family 'Other'); OS uap-core/tests/test_os.yaml:710-715 (same UA -> family 'Other'); device uap-core/tests/test_device.yaml:398-401 (NCSA_Mosaic/2.0(Windows 3.1) -> family 'Other'; uap-core has no device catch-all rule, see moon_ua_parser_lib/tests/semantics/semantics.mbt Test 12 comment).
    • Runtime-verified for all three domains at once by the SomethingWeNeverKnewExisted test in core_tests.mbt.

    Note: a bot UA such as Googlebot reports device family "Spider" because a real device rule produced it (rule 625, uap-core/regexes.yaml:6217) — that is a match, NOT this fallback, and must not trigger degradation by itself.

    REASON_EMPTY_FALLBACK

    let REASON_EMPTY_FALLBACK : String

    Degradation reason recorded when the parse succeeded but all three domain families equal the library's all-miss fallback literal (FALLBACK_FAMILY) — i.e. nothing about the UA was actually learned.

    REASON_PARSE_ERROR

    let REASON_PARSE_ERROR : String

    Degradation reason recorded when the parse result is Err. With the precompiled rule set parse never returns Err today; the reason is kept distinct so adapters can attribute future failure modes.

    UA_SUMMARY_MAX_LENGTH

    let UA_SUMMARY_MAX_LENGTH : Int

    Maximum length of ForensicsRecord.ua_summary in characters. Longer user-agent strings are truncated to exactly this many leading characters by truncate_ua_summary.

    assemble

    Assemble the middleware result for one request (T-03 core entry point). Consumes the library's parse result:

    • Err(_): mounts empty_ua_info() + forensics with reason REASON_PARSE_ERROR. Never re-raises — the caller's response chain continues (spec §3 degradation).
    • Ok(info) where is_empty_fallback(info) (all three families are the library's all-miss fallback): mounts empty_ua_info() + forensics with reason REASON_EMPTY_FALLBACK.
    • otherwise (Ok(info)): mounts info unchanged with no forensics.

    assemble_error

    fn assemble_error(ua : String) -> MiddlewareOutcome

    Err-branch of assemble, also usable directly: mounts empty_ua_info() plus a REASON_PARSE_ERROR forensics record for ua, never raising. Exposed separately because @ua_parser.UaError is a read-only error type (external code cannot construct Err values to test with): an adapter that handles the parse result itself can call this on its Err arm, and unit tests pin the Err behavior through it.

    degradation_reason

    Degradation judgment for a parse result: Some(reason) when the request must degrade — REASON_PARSE_ERROR for Err(_), REASON_EMPTY_FALLBACK when the parsed UaInfo satisfies is_empty_fallback — and None when the parse is healthy. Pure and total: never raises.

    empty_ua_info

    The empty UaInfo mounted on degradation: every domain family is the empty string (meaning "no parse result") and every version/brand/model field is None. This is deliberately distinct from the library's all-miss fallback (FALLBACK_FAMILY = "Other" per domain), so callers can tell "middleware had nothing" from "the library matched nothing".

    is_degraded

    Degradation judgment convenience wrapper: true iff degradation_reason(result) is Some(_).

    is_empty_fallback

    fn is_empty_fallback(info :
    UaInfo
    ) -> Bool

    Degradation judgment for an already-parsed UaInfo: true iff ALL THREE domain families equal the library's all-miss fallback literal (FALLBACK_FAMILY). A single fallback domain (e.g. desktop UAs whose device rules all miss) is NOT degraded — the other domains still carry real information. Partial or exact family matches on any domain keep the result healthy.

    make_forensics

    fn make_forensics(ua : String, reason : String) -> ForensicsRecord

    Construct a forensics record: ua_summary is the original UA truncated to UA_SUMMARY_MAX_LENGTH characters, reason is the degradation reason from degradation_reason. Pure data construction only — the caller (framework thin layer) decides where the record goes.

    truncate_ua_summary

    fn truncate_ua_summary(ua : String) -> String

    Truncate a user-agent string to at most UA_SUMMARY_MAX_LENGTH characters (character-based, not bytes/code units). Strings at or below the limit are returned unchanged.