"""구조물이 선 자리가 **성토면인가 절토면인가** — 판정 한 벌. 왜 있나(2026-09-09) — 품셈 표준경사 표가 **높이 × 메/찰 × 성토/절토** 셋으로 갈리는데, 앞 둘은 구조물 제원에 있고 **성절토만 없었다**. 그 값은 횡단 설계가 아는 것이라 B06 이 내주기로 했고, **새 입력을 만들지 않고** 이미 저장되는 두 값으로 가린다: design.section_mode left_cut · right_cut · both_cut · both_fill 구조물 options.side "자동(성토 쪽)" · "좌" · "우" 판정이 두 곳에 두 벌로 짜이면 `both_cut` + 「자동」의 예외에서 갈린다 — 그래서 여기 하나만 둔다(CLAUDE.md 5장 「코드는 한 벌」). **파이썬 단독**이다: 읽는 쪽이 B08 수량뿐이고 브라우저는 이 판정을 쓰지 않는다. 화면이 쓰게 되면 그때 TS 짝을 만들고 거울 시험을 붙일 것. ⚠ **못 가르면 `None` 을 돌려준다.** 성토로 눅여 돌려주지 않는다 — 받는 쪽이 「근거가 없다」는 사실을 알아야 임의값이 금액으로 굳지 않는다(측구 토사/암 분리와 같은 태도). """ from __future__ import annotations FILL = "성토" CUT = "절토" #: 단면유형 → (좌측 역할, 우측 역할). 좌 = +offset, 우 = −offset (B06 규약). _MODE_ROLES: dict[str, tuple[str, str]] = { "left_cut": (CUT, FILL), "right_cut": (FILL, CUT), "both_cut": (CUT, CUT), "both_fill": (FILL, FILL), } #: 설치 측 표기 — 레지스트리 선택지와 옛 저장분(영문)을 함께 받는다. _LEFT = {"좌", "left", "L"} _RIGHT = {"우", "right", "R"} _AUTO = {"자동(성토 쪽)", "자동", "auto", ""} def structure_face_role(section_mode: str | None, side: str | None) -> tuple[str | None, str]: """(`"성토"` | `"절토"` | `None`, 까닭 문구)를 돌려준다. 까닭 문구는 화면 근거 줄에 그대로 실을 수 있는 한 줄이다 (예: `right_cut · 좌측 → 성토면`). """ mode = (section_mode or "").strip() roles = _MODE_ROLES.get(mode) if roles is None: return None, f"단면유형을 알 수 없음({section_mode!r}) — 성절토를 가를 근거 없음" left_role, right_role = roles picked = (side or "").strip() if picked in _LEFT: return left_role, f"{mode} · 좌측 → {left_role}면" if picked in _RIGHT: return right_role, f"{mode} · 우측 → {right_role}면" if picked in _AUTO: # 「자동(성토 쪽)」은 성토면에 세운다는 뜻이다 — 그런데 양쪽이 다 절토면 # 세울 성토면이 없어 어느 쪽인지 정할 근거가 없다. 임의로 몰지 않는다. if left_role == CUT and right_role == CUT: return None, f"{mode} · 자동(성토 쪽)인데 성토면이 없음 — 가를 근거 없음" return FILL, f"{mode} · 자동(성토 쪽) → 성토면" return None, f"설치 측을 알 수 없음({side!r}) — 성절토를 가를 근거 없음"