slicer_strings.py 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. """Slicer string-list options as plain lists (#3380)."""
  2. def slicer_strings(raw: object) -> list[str]:
  3. """A slicer string-list option as its non-empty strings.
  4. Orca Cloud returns a list the way the slicer writes it to its INI files —
  5. one string of quoted, ``\\``-escaped items joined by ``;``
  6. (``"Bambu Lab X1 Carbon 0.4 nozzle";"Bambu Lab H2C 0.4 nozzle"``). Left
  7. quoted, a ``compatible_printers`` name never equals the selected printer
  8. preset and the SliceModal hides the profile for every printer. A JSON
  9. array is never INI-encoded, so its items are kept as they are. A string
  10. the slicer itself can't read counts as no data rather than failing the
  11. caller.
  12. """
  13. if isinstance(raw, list):
  14. items = [s for s in raw if isinstance(s, str)]
  15. elif isinstance(raw, str):
  16. items = unescape_strings_cstyle(raw) or []
  17. else:
  18. return []
  19. return [s for s in items if s.strip()]
  20. def first_slicer_string(raw: object) -> str | None:
  21. """The first non-empty item of a slicer string-list option, stripped.
  22. Filament profiles store per-extruder options such as ``filament_type`` as
  23. one-element lists; pre-pick matching only needs the first slot.
  24. """
  25. return next((s.strip() for s in slicer_strings(raw)), None)
  26. def unescape_strings_cstyle(text: str) -> list[str] | None:
  27. """Port of OrcaSlicer's ``unescape_strings_cstyle`` (libslic3r/Config.cpp).
  28. Unquoted items are read up to ``;`` as is; quoted items decode ``\\n``,
  29. ``\\r`` and ``\\<char>``. Empty items are kept, so per-extruder positions
  30. survive. Returns ``None`` where the slicer reports a parse failure: an
  31. unterminated quote, a trailing ``\\`` inside quotes, or anything but
  32. spaces, tabs and ``;`` after a closing quote.
  33. """
  34. out: list[str] = []
  35. i, n = 0, len(text)
  36. if n == 0:
  37. return out
  38. while True:
  39. while text[i] in " \t":
  40. i += 1
  41. if i == n:
  42. return out
  43. buf: list[str] = []
  44. if text[i] == '"':
  45. i += 1
  46. while i < n and text[i] != '"':
  47. c = text[i]
  48. if c == "\\":
  49. i += 1
  50. if i == n:
  51. return None
  52. c = {"r": "\r", "n": "\n"}.get(text[i], text[i])
  53. buf.append(c)
  54. i += 1
  55. if i == n:
  56. return None
  57. i += 1
  58. else:
  59. while i < n and text[i] != ";":
  60. buf.append(text[i])
  61. i += 1
  62. out.append("".join(buf))
  63. if i == n:
  64. return out
  65. while text[i] in " \t":
  66. i += 1
  67. if i == n:
  68. return out
  69. if text[i] != ";":
  70. return None
  71. i += 1
  72. if i == n:
  73. out.append("")
  74. return out