refactor: replace custom lens parsing with panproto-lens-dsl lens_config.rs is now a thin wrapper around panproto_lens_dsl: - load_dir() scans JSON/YAML/Nickel lens files - compile() produces CompiledLens with chain + field_transforms - No custom step parsing, expression parsing, or value conversion All panproto crates switched to path dependencies from phrom checkout for development. Will switch to crates.io versions when released. Removed panproto-expr-parser from direct deps (used transitively).

Author: Aaron Steven White
Commit afbe1568a46e75544334e7f1375fc9964ab3c6d4
Parent: f0a0bf57fd
Structural diff unavailable

These commits were pushed via plain git push, so no pre-parsed schemas are available. Install git-remote-cospan and re-push via panproto:// to see scope-level changes, breaking change detection, and semantic diffs.

brew install panproto/tap/git-remote-cospan
6 files changed +882 -332
@@ -39,6 +39,12 @@ dependencies = [
3939 ]
4040 
4141 [[package]]
42+name = "aliasable"
43+version = "0.1.3"
44+source = "registry+https://github.com/rust-lang/crates.io-index"
45+checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
46+
47+[[package]]
4248 name = "allocator-api2"
4349 version = "0.2.21"
4450 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -84,6 +90,12 @@ dependencies = [
8490 ]
8591 
8692 [[package]]
93+name = "arraydeque"
94+version = "0.5.1"
95+source = "registry+https://github.com/rust-lang/crates.io-index"
96+checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236"
97+
98+[[package]]
8799 name = "arrayref"
88100 version = "0.3.9"
89101 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -91,11 +103,26 @@ checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
91103 
92104 [[package]]
93105 name = "arrayvec"
106+version = "0.5.2"
107+source = "registry+https://github.com/rust-lang/crates.io-index"
108+checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
109+
110+[[package]]
111+name = "arrayvec"
94112 version = "0.7.6"
95113 source = "registry+https://github.com/rust-lang/crates.io-index"
96114 checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
97115 
98116 [[package]]
117+name = "ascii-canvas"
118+version = "4.0.0"
119+source = "registry+https://github.com/rust-lang/crates.io-index"
120+checksum = "ef1e3e699d84ab1b0911a1010c5c106aa34ae89aeac103be5ce0c3859db1e891"
121+dependencies = [
122+ "term",
123+]
124+
125+[[package]]
99126 name = "async-compression"
100127 version = "0.4.41"
101128 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -245,6 +272,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
245272 checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
246273 
247274 [[package]]
275+name = "beef"
276+version = "0.5.2"
277+source = "registry+https://github.com/rust-lang/crates.io-index"
278+checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1"
279+
280+[[package]]
281+name = "bit-set"
282+version = "0.8.0"
283+source = "registry+https://github.com/rust-lang/crates.io-index"
284+checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
285+dependencies = [
286+ "bit-vec",
287+]
288+
289+[[package]]
290+name = "bit-vec"
291+version = "0.8.0"
292+source = "registry+https://github.com/rust-lang/crates.io-index"
293+checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
294+
295+[[package]]
248296 name = "bitflags"
249297 version = "2.11.0"
250298 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -254,13 +302,19 @@ dependencies = [
254302 ]
255303 
256304 [[package]]
305+name = "bitmaps"
306+version = "3.2.1"
307+source = "registry+https://github.com/rust-lang/crates.io-index"
308+checksum = "a1d084b0137aaa901caf9f1e8b21daa6aa24d41cd806e111335541eff9683bd6"
309+
310+[[package]]
257311 name = "blake3"
258312 version = "1.8.4"
259313 source = "registry+https://github.com/rust-lang/crates.io-index"
260314 checksum = "4d2d5991425dfd0785aed03aedcf0b321d61975c9b5b3689c774a2610ae0b51e"
261315 dependencies = [
262316  "arrayref",
263- "arrayvec",
317+ "arrayvec 0.7.6",
264318  "cc",
265319  "cfg-if",
266320  "constant_time_eq",
@@ -293,6 +347,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
293347 checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
294348 
295349 [[package]]
350+name = "bytemuck"
351+version = "1.25.0"
352+source = "registry+https://github.com/rust-lang/crates.io-index"
353+checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec"
354+
355+[[package]]
296356 name = "byteorder"
297357 version = "1.5.0"
298358 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -357,6 +417,33 @@ dependencies = [
357417 ]
358418 
359419 [[package]]
420+name = "codespan"
421+version = "0.13.1"
422+source = "registry+https://github.com/rust-lang/crates.io-index"
423+checksum = "583f52b0658b321b25fd6b209b6c76cf058f433071297de64e5980c3d9aad937"
424+dependencies = [
425+ "codespan-reporting",
426+ "serde",
427+]
428+
429+[[package]]
430+name = "codespan-reporting"
431+version = "0.13.1"
432+source = "registry+https://github.com/rust-lang/crates.io-index"
433+checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681"
434+dependencies = [
435+ "serde",
436+ "termcolor",
437+ "unicode-width 0.2.2",
438+]
439+
440+[[package]]
441+name = "colorchoice"
442+version = "1.0.5"
443+source = "registry+https://github.com/rust-lang/crates.io-index"
444+checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
445+
446+[[package]]
360447 name = "combine"
361448 version = "4.6.7"
362449 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -490,6 +577,7 @@ dependencies = [
490577  "panproto-gat",
491578  "panproto-inst",
492579  "panproto-lens",
580+ "panproto-lens-dsl",
493581  "panproto-mig",
494582  "panproto-protocols",
495583  "panproto-schema",
@@ -527,7 +615,7 @@ dependencies = [
527615  "tempfile",
528616  "thiserror",
529617  "tokio",
530- "toml",
618+ "toml 0.8.23",
531619  "tower",
532620  "tower-http",
533621  "tracing",
@@ -760,6 +848,15 @@ dependencies = [
760848 ]
761849 
762850 [[package]]
851+name = "ena"
852+version = "0.14.4"
853+source = "registry+https://github.com/rust-lang/crates.io-index"
854+checksum = "eabffdaee24bd1bf95c5ef7cec31260444317e72ea56c4c91750e8b7ee58d5f1"
855+dependencies = [
856+ "log",
857+]
858+
859+[[package]]
763860 name = "encoding_rs"
764861 version = "0.8.35"
765862 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -839,6 +936,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
839936 checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
840937 
841938 [[package]]
939+name = "fixedbitset"
940+version = "0.5.7"
941+source = "registry+https://github.com/rust-lang/crates.io-index"
942+checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
943+
944+[[package]]
842945 name = "flate2"
843946 version = "1.1.9"
844947 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1165,6 +1268,12 @@ dependencies = [
11651268 
11661269 [[package]]
11671270 name = "heck"
1271+version = "0.4.1"
1272+source = "registry+https://github.com/rust-lang/crates.io-index"
1273+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
1274+
1275+[[package]]
1276+name = "heck"
11681277 version = "0.5.0"
11691278 source = "registry+https://github.com/rust-lang/crates.io-index"
11701279 checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
@@ -1460,6 +1569,15 @@ dependencies = [
14601569 ]
14611570 
14621571 [[package]]
1572+name = "imbl-sized-chunks"
1573+version = "0.1.3"
1574+source = "registry+https://github.com/rust-lang/crates.io-index"
1575+checksum = "8f4241005618a62f8d57b2febd02510fb96e0137304728543dfc5fd6f052c22d"
1576+dependencies = [
1577+ "bitmaps",
1578+]
1579+
1580+[[package]]
14631581 name = "indexmap"
14641582 version = "2.13.0"
14651583 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1472,6 +1590,15 @@ dependencies = [
14721590 ]
14731591 
14741592 [[package]]
1593+name = "indoc"
1594+version = "2.0.7"
1595+source = "registry+https://github.com/rust-lang/crates.io-index"
1596+checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
1597+dependencies = [
1598+ "rustversion",
1599+]
1600+
1601+[[package]]
14751602 name = "ipnet"
14761603 version = "2.12.0"
14771604 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1503,6 +1630,15 @@ dependencies = [
15031630 ]
15041631 
15051632 [[package]]
1633+name = "itertools"
1634+version = "0.14.0"
1635+source = "registry+https://github.com/rust-lang/crates.io-index"
1636+checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
1637+dependencies = [
1638+ "either",
1639+]
1640+
1641+[[package]]
15061642 name = "itoa"
15071643 version = "1.0.18"
15081644 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1531,6 +1667,15 @@ dependencies = [
15311667 ]
15321668 
15331669 [[package]]
1670+name = "json_scanner"
1671+version = "0.1.0"
1672+source = "registry+https://github.com/rust-lang/crates.io-index"
1673+checksum = "fe0a2dc336065c75719cffd3c6c929e0ec4ed85b92b8248a7bbd999acb0e419c"
1674+dependencies = [
1675+ "memchr",
1676+]
1677+
1678+[[package]]
15341679 name = "jsonwebtoken"
15351680 version = "9.3.1"
15361681 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1546,6 +1691,47 @@ dependencies = [
15461691 ]
15471692 
15481693 [[package]]
1694+name = "keccak"
1695+version = "0.1.6"
1696+source = "registry+https://github.com/rust-lang/crates.io-index"
1697+checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653"
1698+dependencies = [
1699+ "cpufeatures 0.2.17",
1700+]
1701+
1702+[[package]]
1703+name = "lalrpop"
1704+version = "0.22.2"
1705+source = "registry+https://github.com/rust-lang/crates.io-index"
1706+checksum = "ba4ebbd48ce411c1d10fb35185f5a51a7bfa3d8b24b4e330d30c9e3a34129501"
1707+dependencies = [
1708+ "ascii-canvas",
1709+ "bit-set",
1710+ "ena",
1711+ "itertools 0.14.0",
1712+ "lalrpop-util",
1713+ "petgraph",
1714+ "pico-args",
1715+ "regex",
1716+ "regex-syntax 0.8.10",
1717+ "sha3",
1718+ "string_cache",
1719+ "term",
1720+ "unicode-xid",
1721+ "walkdir",
1722+]
1723+
1724+[[package]]
1725+name = "lalrpop-util"
1726+version = "0.22.2"
1727+source = "registry+https://github.com/rust-lang/crates.io-index"
1728+checksum = "b5baa5e9ff84f1aefd264e6869907646538a52147a755d494517a8007fb48733"
1729+dependencies = [
1730+ "regex-automata 0.4.14",
1731+ "rustversion",
1732+]
1733+
1734+[[package]]
15491735 name = "lazy_static"
15501736 version = "1.5.0"
15511737 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1623,6 +1809,12 @@ dependencies = [
16231809 ]
16241810 
16251811 [[package]]
1812+name = "libyaml-rs"
1813+version = "0.3.0"
1814+source = "registry+https://github.com/rust-lang/crates.io-index"
1815+checksum = "2e126dda6f34391ab7b444f9922055facc83c07a910da3eb16f1e4d9c45dc777"
1816+
1817+[[package]]
16261818 name = "libz-sys"
16271819 version = "1.1.25"
16281820 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1663,11 +1855,36 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
16631855 
16641856 [[package]]
16651857 name = "logos"
1858+version = "0.15.1"
1859+source = "registry+https://github.com/rust-lang/crates.io-index"
1860+checksum = "ff472f899b4ec2d99161c51f60ff7075eeb3097069a36050d8037a6325eb8154"
1861+dependencies = [
1862+ "logos-derive 0.15.1",
1863+]
1864+
1865+[[package]]
1866+name = "logos"
16661867 version = "0.16.1"
16671868 source = "registry+https://github.com/rust-lang/crates.io-index"
16681869 checksum = "eb2c55a318a87600ea870ff8c2012148b44bf18b74fad48d0f835c38c7d07c5f"
16691870 dependencies = [
1670- "logos-derive",
1871+ "logos-derive 0.16.1",
1872+]
1873+
1874+[[package]]
1875+name = "logos-codegen"
1876+version = "0.15.1"
1877+source = "registry+https://github.com/rust-lang/crates.io-index"
1878+checksum = "192a3a2b90b0c05b27a0b2c43eecdb7c415e29243acc3f89cc8247a5b693045c"
1879+dependencies = [
1880+ "beef",
1881+ "fnv",
1882+ "lazy_static",
1883+ "proc-macro2",
1884+ "quote",
1885+ "regex-syntax 0.8.10",
1886+ "rustc_version",
1887+ "syn",
16711888 ]
16721889 
16731890 [[package]]
@@ -1686,11 +1903,20 @@ dependencies = [
16861903 
16871904 [[package]]
16881905 name = "logos-derive"
1906+version = "0.15.1"
1907+source = "registry+https://github.com/rust-lang/crates.io-index"
1908+checksum = "605d9697bcd5ef3a42d38efc51541aa3d6a4a25f7ab6d1ed0da5ac632a26b470"
1909+dependencies = [
1910+ "logos-codegen 0.15.1",
1911+]
1912+
1913+[[package]]
1914+name = "logos-derive"
16891915 version = "0.16.1"
16901916 source = "registry+https://github.com/rust-lang/crates.io-index"
16911917 checksum = "52d3a9855747c17eaf4383823f135220716ab49bea5fbea7dd42cc9a92f8aa31"
16921918 dependencies = [
1693- "logos-codegen",
1919+ "logos-codegen 0.16.1",
16941920 ]
16951921 
16961922 [[package]]
@@ -1700,6 +1926,68 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
17001926 checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
17011927 
17021928 [[package]]
1929+name = "malachite"
1930+version = "0.6.1"
1931+source = "registry+https://github.com/rust-lang/crates.io-index"
1932+checksum = "ec410515e231332b14cd986a475d1c3323bcfa4c7efc038bfa1d5b410b1c57e4"
1933+dependencies = [
1934+ "malachite-base",
1935+ "malachite-float",
1936+ "malachite-nz",
1937+ "malachite-q",
1938+]
1939+
1940+[[package]]
1941+name = "malachite-base"
1942+version = "0.6.1"
1943+source = "registry+https://github.com/rust-lang/crates.io-index"
1944+checksum = "c738d3789301e957a8f7519318fcbb1b92bb95863b28f6938ae5a05be6259f34"
1945+dependencies = [
1946+ "hashbrown 0.15.5",
1947+ "itertools 0.14.0",
1948+ "libm",
1949+ "ryu",
1950+]
1951+
1952+[[package]]
1953+name = "malachite-float"
1954+version = "0.6.1"
1955+source = "registry+https://github.com/rust-lang/crates.io-index"
1956+checksum = "9446a966be7f1708c10badd6690d3094b5ad62d3accdcf2154740656d7650cfa"
1957+dependencies = [
1958+ "itertools 0.14.0",
1959+ "malachite-base",
1960+ "malachite-nz",
1961+ "malachite-q",
1962+ "serde",
1963+]
1964+
1965+[[package]]
1966+name = "malachite-nz"
1967+version = "0.6.1"
1968+source = "registry+https://github.com/rust-lang/crates.io-index"
1969+checksum = "1707c9a1fa36ce21749b35972bfad17bbf34cf5a7c96897c0491da321e387d3b"
1970+dependencies = [
1971+ "itertools 0.14.0",
1972+ "libm",
1973+ "malachite-base",
1974+ "serde",
1975+ "wide",
1976+]
1977+
1978+[[package]]
1979+name = "malachite-q"
1980+version = "0.6.1"
1981+source = "registry+https://github.com/rust-lang/crates.io-index"
1982+checksum = "d764801aa4e96bbb69b389dcd03b50075345131cd63ca2e380bca71cc37a3675"
1983+dependencies = [
1984+ "itertools 0.14.0",
1985+ "malachite-base",
1986+ "malachite-nz",
1987+ "serde",
1988+]
1989+
1990+[[package]]
17031991 name = "matchers"
17041992 version = "0.2.0"
17051993 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1851,6 +2139,108 @@ dependencies = [
18512139 ]
18522140 
18532141 [[package]]
2142+name = "new_debug_unreachable"
2143+version = "1.0.6"
2144+source = "registry+https://github.com/rust-lang/crates.io-index"
2145+checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
2146+
2147+[[package]]
2148+name = "nickel-lang"
2149+version = "2.0.0"
2150+source = "registry+https://github.com/rust-lang/crates.io-index"
2151+checksum = "ec629a17e21b7e192dfc715a9567fb4f773a6d0c92424f0ec659c6633f31b087"
2152+dependencies = [
2153+ "codespan-reporting",
2154+ "indexmap",
2155+ "malachite",
2156+ "nickel-lang-core",
2157+ "nickel-lang-vector",
2158+ "serde",
2159+ "serde_json",
2160+ "serde_yaml",
2161+ "toml 0.9.12+spec-1.1.0",
2162+]
2163+
2164+[[package]]
2165+name = "nickel-lang-core"
2166+version = "0.16.1"
2167+source = "registry+https://github.com/rust-lang/crates.io-index"
2168+checksum = "51647f09e6e385c140226867c62292f23c31241ee2b4986f3f71a40d48e88a60"
2169+dependencies = [
2170+ "base64",
2171+ "bumpalo",
2172+ "codespan",
2173+ "codespan-reporting",
2174+ "colorchoice",
2175+ "indexmap",
2176+ "indoc",
2177+ "json_scanner",
2178+ "lalrpop",
2179+ "lalrpop-util",
2180+ "logos 0.15.1",
2181+ "malachite",
2182+ "malachite-q",
2183+ "md-5",
2184+ "nickel-lang-parser",
2185+ "nickel-lang-vector",
2186+ "once_cell",
2187+ "ouroboros",
2188+ "paste",
2189+ "pretty",
2190+ "regex",
2191+ "saphyr-parser",
2192+ "serde",
2193+ "serde_json",
2194+ "serde_yaml",
2195+ "sha-1",
2196+ "sha2",
2197+ "simple-counter",
2198+ "smallvec 1.15.1",
2199+ "strip-ansi-escapes",
2200+ "strsim",
2201+ "toml 0.9.12+spec-1.1.0",
2202+ "toml_edit 0.23.10+spec-1.0.0",
2203+ "typed-arena",
2204+ "unicode-segmentation",
2205+]
2206+
2207+[[package]]
2208+name = "nickel-lang-parser"
2209+version = "0.1.1"
2210+source = "registry+https://github.com/rust-lang/crates.io-index"
2211+checksum = "20d8810705f1a243b1996c83fe9f173d8fb33f1cc97ea5ff7486cc6ef8981f81"
2212+dependencies = [
2213+ "bumpalo",
2214+ "codespan",
2215+ "codespan-reporting",
2216+ "indexmap",
2217+ "lalrpop",
2218+ "lalrpop-util",
2219+ "logos 0.15.1",
2220+ "malachite",
2221+ "nickel-lang-vector",
2222+ "ouroboros",
2223+ "pretty",
2224+ "regex",
2225+ "saphyr-parser",
2226+ "serde",
2227+ "serde_json",
2228+ "simple-counter",
2229+ "toml_edit 0.23.10+spec-1.0.0",
2230+ "typed-arena",
2231+]
2232+
2233+[[package]]
2234+name = "nickel-lang-vector"
2235+version = "0.1.0"
2236+source = "registry+https://github.com/rust-lang/crates.io-index"
2237+checksum = "870c323d81061fc47db4aa7346f6f3492f3e4bb8bd5dd8b7c85ac9d0c9709f0c"
2238+dependencies = [
2239+ "imbl-sized-chunks",
2240+ "serde",
2241+]
2242+
2243+[[package]]
18542244 name = "nu-ansi-term"
18552245 version = "0.50.3"
18562246 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1993,6 +2383,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
19932383 checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
19942384 
19952385 [[package]]
2386+name = "ouroboros"
2387+version = "0.18.5"
2388+source = "registry+https://github.com/rust-lang/crates.io-index"
2389+checksum = "1e0f050db9c44b97a94723127e6be766ac5c340c48f2c4bb3ffa11713744be59"
2390+dependencies = [
2391+ "aliasable",
2392+ "ouroboros_macro",
2393+ "static_assertions",
2394+]
2395+
2396+[[package]]
2397+name = "ouroboros_macro"
2398+version = "0.18.5"
2399+source = "registry+https://github.com/rust-lang/crates.io-index"
2400+checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0"
2401+dependencies = [
2402+ "heck 0.4.1",
2403+ "proc-macro2",
2404+ "proc-macro2-diagnostics",
2405+ "quote",
2406+ "syn",
2407+]
2408+
2409+[[package]]
19962410 name = "owo-colors"
19972411 version = "4.3.0"
19982412 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2012,8 +2426,7 @@ dependencies = [
20122426 
20132427 [[package]]
20142428 name = "panproto-check"
2015-version = "0.23.0"
2016-source = "git+https://github.com/panproto/panproto.git?tag=v0.23.0#be3c299556210beccc02113c870734cbfe6bde4e"
2429+version = "0.24.0"
20172430 dependencies = [
20182431  "panproto-gat",
20192432  "panproto-lens",
@@ -2027,8 +2440,7 @@ dependencies = [
20272440 
20282441 [[package]]
20292442 name = "panproto-core"
2030-version = "0.23.0"
2031-source = "git+https://github.com/panproto/panproto.git?tag=v0.23.0#be3c299556210beccc02113c870734cbfe6bde4e"
2443+version = "0.24.0"
20322444 dependencies = [
20332445  "panproto-check",
20342446  "panproto-gat",
@@ -2043,8 +2455,7 @@ dependencies = [
20432455 
20442456 [[package]]
20452457 name = "panproto-expr"
2046-version = "0.23.0"
2047-source = "git+https://github.com/panproto/panproto.git?tag=v0.23.0#be3c299556210beccc02113c870734cbfe6bde4e"
2458+version = "0.24.0"
20482459 dependencies = [
20492460  "rustc-hash",
20502461  "serde",
@@ -2053,18 +2464,16 @@ dependencies = [
20532464 
20542465 [[package]]
20552466 name = "panproto-expr-parser"
2056-version = "0.23.0"
2057-source = "git+https://github.com/panproto/panproto.git?tag=v0.23.0#be3c299556210beccc02113c870734cbfe6bde4e"
2467+version = "0.24.0"
20582468 dependencies = [
20592469  "chumsky",
2060- "logos",
2470+ "logos 0.16.1",
20612471  "panproto-expr",
20622472 ]
20632473 
20642474 [[package]]
20652475 name = "panproto-gat"
2066-version = "0.23.0"
2067-source = "git+https://github.com/panproto/panproto.git?tag=v0.23.0#be3c299556210beccc02113c870734cbfe6bde4e"
2476+version = "0.24.0"
20682477 dependencies = [
20692478  "panproto-expr",
20702479  "rustc-hash",
@@ -2074,8 +2483,7 @@ dependencies = [
20742483 
20752484 [[package]]
20762485 name = "panproto-git"
2077-version = "0.23.0"
2078-source = "git+https://github.com/panproto/panproto.git?tag=v0.23.0#be3c299556210beccc02113c870734cbfe6bde4e"
2486+version = "0.24.0"
20792487 dependencies = [
20802488  "git2",
20812489  "miette",
@@ -2092,20 +2500,18 @@ dependencies = [
20922500 
20932501 [[package]]
20942502 name = "panproto-grammars"
2095-version = "0.23.0"
2096-source = "git+https://github.com/panproto/panproto.git?tag=v0.23.0#be3c299556210beccc02113c870734cbfe6bde4e"
2503+version = "0.24.0"
20972504 dependencies = [
20982505  "cc",
20992506  "serde",
2100- "toml",
2507+ "toml 0.8.23",
21012508  "tree-sitter",
21022509  "tree-sitter-language",
21032510 ]
21042511 
21052512 [[package]]
21062513 name = "panproto-inst"
2107-version = "0.23.0"
2108-source = "git+https://github.com/panproto/panproto.git?tag=v0.23.0#be3c299556210beccc02113c870734cbfe6bde4e"
2514+version = "0.24.0"
21092515 dependencies = [
21102516  "bumpalo",
21112517  "panproto-expr",
@@ -2120,8 +2526,7 @@ dependencies = [
21202526 
21212527 [[package]]
21222528 name = "panproto-io"
2123-version = "0.23.0"
2124-source = "git+https://github.com/panproto/panproto.git?tag=v0.23.0#be3c299556210beccc02113c870734cbfe6bde4e"
2529+version = "0.24.0"
21252530 dependencies = [
21262531  "bumpalo",
21272532  "memchr",
@@ -2143,8 +2548,7 @@ dependencies = [
21432548 
21442549 [[package]]
21452550 name = "panproto-lens"
2146-version = "0.23.0"
2147-source = "git+https://github.com/panproto/panproto.git?tag=v0.23.0#be3c299556210beccc02113c870734cbfe6bde4e"
2551+version = "0.24.0"
21482552 dependencies = [
21492553  "panproto-expr",
21502554  "panproto-gat",
@@ -2159,9 +2563,26 @@ dependencies = [
21592563 ]
21602564 
21612565 [[package]]
2566+name = "panproto-lens-dsl"
2567+version = "0.24.0"
2568+dependencies = [
2569+ "miette",
2570+ "nickel-lang",
2571+ "panproto-expr",
2572+ "panproto-expr-parser",
2573+ "panproto-gat",
2574+ "panproto-inst",
2575+ "panproto-lens",
2576+ "serde",
2577+ "serde_json",
2578+ "tempfile",
2579+ "thiserror",
2580+ "yaml_serde",
2581+]
2582+
2583+[[package]]
21622584 name = "panproto-mig"
2163-version = "0.23.0"
2164-source = "git+https://github.com/panproto/panproto.git?tag=v0.23.0#be3c299556210beccc02113c870734cbfe6bde4e"
2585+version = "0.24.0"
21652586 dependencies = [
21662587  "panproto-expr",
21672588  "panproto-gat",
@@ -2174,8 +2595,7 @@ dependencies = [
21742595 
21752596 [[package]]
21762597 name = "panproto-parse"
2177-version = "0.23.0"
2178-source = "git+https://github.com/panproto/panproto.git?tag=v0.23.0#be3c299556210beccc02113c870734cbfe6bde4e"
2598+version = "0.24.0"
21792599 dependencies = [
21802600  "memchr",
21812601  "miette",
@@ -2193,8 +2613,7 @@ dependencies = [
21932613 
21942614 [[package]]
21952615 name = "panproto-project"
2196-version = "0.23.0"
2197-source = "git+https://github.com/panproto/panproto.git?tag=v0.23.0#be3c299556210beccc02113c870734cbfe6bde4e"
2616+version = "0.24.0"
21982617 dependencies = [
21992618  "blake3",
22002619  "globset",
@@ -2208,13 +2627,12 @@ dependencies = [
22082627  "serde_json",
22092628  "smallvec 2.0.0-alpha.12",
22102629  "thiserror",
2211- "toml",
2630+ "toml 0.8.23",
22122631 ]
22132632 
22142633 [[package]]
22152634 name = "panproto-protocols"
2216-version = "0.23.0"
2217-source = "git+https://github.com/panproto/panproto.git?tag=v0.23.0#be3c299556210beccc02113c870734cbfe6bde4e"
2635+version = "0.24.0"
22182636 dependencies = [
22192637  "blake3",
22202638  "panproto-gat",
@@ -2228,8 +2646,7 @@ dependencies = [
22282646 
22292647 [[package]]
22302648 name = "panproto-schema"
2231-version = "0.23.0"
2232-source = "git+https://github.com/panproto/panproto.git?tag=v0.23.0#be3c299556210beccc02113c870734cbfe6bde4e"
2649+version = "0.24.0"
22332650 dependencies = [
22342651  "panproto-expr",
22352652  "panproto-gat",
@@ -2241,8 +2658,7 @@ dependencies = [
22412658 
22422659 [[package]]
22432660 name = "panproto-vcs"
2244-version = "0.23.0"
2245-source = "git+https://github.com/panproto/panproto.git?tag=v0.23.0#be3c299556210beccc02113c870734cbfe6bde4e"
2661+version = "0.24.0"
22462662 dependencies = [
22472663  "blake3",
22482664  "panproto-check",
@@ -2262,8 +2678,7 @@ dependencies = [
22622678 
22632679 [[package]]
22642680 name = "panproto-xrpc"
2265-version = "0.23.0"
2266-source = "git+https://github.com/panproto/panproto.git?tag=v0.23.0#be3c299556210beccc02113c870734cbfe6bde4e"
2681+version = "0.24.0"
22672682 dependencies = [
22682683  "hex",
22692684  "miette",
@@ -2307,6 +2722,12 @@ dependencies = [
23072722 ]
23082723 
23092724 [[package]]
2725+name = "paste"
2726+version = "1.0.15"
2727+source = "registry+https://github.com/rust-lang/crates.io-index"
2728+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
2729+
2730+[[package]]
23102731 name = "pem"
23112732 version = "3.0.6"
23122733 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2332,6 +2753,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
23322753 checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
23332754 
23342755 [[package]]
2756+name = "petgraph"
2757+version = "0.7.1"
2758+source = "registry+https://github.com/rust-lang/crates.io-index"
2759+checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772"
2760+dependencies = [
2761+ "fixedbitset",
2762+ "indexmap",
2763+]
2764+
2765+[[package]]
2766+name = "phf_shared"
2767+version = "0.11.3"
2768+source = "registry+https://github.com/rust-lang/crates.io-index"
2769+checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
2770+dependencies = [
2771+ "siphasher",
2772+]
2773+
2774+[[package]]
2775+name = "pico-args"
2776+version = "0.5.0"
2777+source = "registry+https://github.com/rust-lang/crates.io-index"
2778+checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
2779+
2780+[[package]]
23352781 name = "pin-project-lite"
23362782 version = "0.2.17"
23372783 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2401,6 +2847,12 @@ dependencies = [
24012847 ]
24022848 
24032849 [[package]]
2850+name = "precomputed-hash"
2851+version = "0.1.1"
2852+source = "registry+https://github.com/rust-lang/crates.io-index"
2853+checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
2854+
2855+[[package]]
24042856 name = "predicates"
24052857 version = "3.1.4"
24062858 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2427,6 +2879,17 @@ dependencies = [
24272879 ]
24282880 
24292881 [[package]]
2882+name = "pretty"
2883+version = "0.12.5"
2884+source = "registry+https://github.com/rust-lang/crates.io-index"
2885+checksum = "0d22152487193190344590e4f30e219cf3fe140d9e7a3fdb683d82aa2c5f4156"
2886+dependencies = [
2887+ "arrayvec 0.5.2",
2888+ "typed-arena",
2889+ "unicode-width 0.2.2",
2890+]
2891+
2892+[[package]]
24302893 name = "prettyplease"
24312894 version = "0.2.37"
24322895 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2455,6 +2918,19 @@ dependencies = [
24552918 ]
24562919 
24572920 [[package]]
2921+name = "proc-macro2-diagnostics"
2922+version = "0.10.1"
2923+source = "registry+https://github.com/rust-lang/crates.io-index"
2924+checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
2925+dependencies = [
2926+ "proc-macro2",
2927+ "quote",
2928+ "syn",
2929+ "version_check",
2930+ "yansi",
2931+]
2932+
2933+[[package]]
24582934 name = "psm"
24592935 version = "0.1.30"
24602936 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2638,7 +3114,7 @@ dependencies = [
26383114  "bytes",
26393115  "combine",
26403116  "futures-util",
2641- "itertools",
3117+ "itertools 0.13.0",
26423118  "itoa",
26433119  "num-bigint",
26443120  "percent-encoding",
@@ -2866,6 +3342,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
28663342 checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe"
28673343 
28683344 [[package]]
3345+name = "rustc_version"
3346+version = "0.4.1"
3347+source = "registry+https://github.com/rust-lang/crates.io-index"
3348+checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
3349+dependencies = [
3350+ "semver",
3351+]
3352+
3353+[[package]]
28693354 name = "rustix"
28703355 version = "1.1.4"
28713356 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2926,6 +3411,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
29263411 checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
29273412 
29283413 [[package]]
3414+name = "safe_arch"
3415+version = "0.7.4"
3416+source = "registry+https://github.com/rust-lang/crates.io-index"
3417+checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323"
3418+dependencies = [
3419+ "bytemuck",
3420+]
3421+
3422+[[package]]
3423+name = "same-file"
3424+version = "1.0.6"
3425+source = "registry+https://github.com/rust-lang/crates.io-index"
3426+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
3427+dependencies = [
3428+ "winapi-util",
3429+]
3430+
3431+[[package]]
3432+name = "saphyr-parser"
3433+version = "0.0.6"
3434+source = "registry+https://github.com/rust-lang/crates.io-index"
3435+checksum = "4fb771b59f6b1985d1406325ec28f97cfb14256abcec4fdfb37b36a1766d6af7"
3436+dependencies = [
3437+ "arraydeque",
3438+ "hashlink",
3439+]
3440+
3441+[[package]]
29293442 name = "schannel"
29303443 version = "0.1.29"
29313444 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3049,6 +3562,15 @@ dependencies = [
30493562 ]
30503563 
30513564 [[package]]
3565+name = "serde_spanned"
3566+version = "1.1.1"
3567+source = "registry+https://github.com/rust-lang/crates.io-index"
3568+checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
3569+dependencies = [
3570+ "serde_core",
3571+]
3572+
3573+[[package]]
30523574 name = "serde_urlencoded"
30533575 version = "0.7.1"
30543576 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3061,6 +3583,19 @@ dependencies = [
30613583 ]
30623584 
30633585 [[package]]
3586+name = "serde_yaml"
3587+version = "0.9.34+deprecated"
3588+source = "registry+https://github.com/rust-lang/crates.io-index"
3589+checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
3590+dependencies = [
3591+ "indexmap",
3592+ "itoa",
3593+ "ryu",
3594+ "serde",
3595+ "unsafe-libyaml",
3596+]
3597+
3598+[[package]]
30643599 name = "serdect"
30653600 version = "0.2.0"
30663601 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3071,6 +3606,17 @@ dependencies = [
30713606 ]
30723607 
30733608 [[package]]
3609+name = "sha-1"
3610+version = "0.10.1"
3611+source = "registry+https://github.com/rust-lang/crates.io-index"
3612+checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c"
3613+dependencies = [
3614+ "cfg-if",
3615+ "cpufeatures 0.2.17",
3616+ "digest",
3617+]
3618+
3619+[[package]]
30743620 name = "sha1"
30753621 version = "0.10.6"
30763622 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3099,6 +3645,16 @@ dependencies = [
30993645 ]
31003646 
31013647 [[package]]
3648+name = "sha3"
3649+version = "0.10.8"
3650+source = "registry+https://github.com/rust-lang/crates.io-index"
3651+checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
3652+dependencies = [
3653+ "digest",
3654+ "keccak",
3655+]
3656+
3657+[[package]]
31023658 name = "sharded-slab"
31033659 version = "0.1.7"
31043660 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3161,6 +3717,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
31613717 checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
31623718 
31633719 [[package]]
3720+name = "simple-counter"
3721+version = "0.1.0"
3722+source = "registry+https://github.com/rust-lang/crates.io-index"
3723+checksum = "4bb57743b52ea059937169c0061d70298fe2df1d2c988b44caae79dd979d9b49"
3724+
3725+[[package]]
31643726 name = "simple_asn1"
31653727 version = "0.6.4"
31663728 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3173,6 +3735,12 @@ dependencies = [
31733735 ]
31743736 
31753737 [[package]]
3738+name = "siphasher"
3739+version = "1.0.2"
3740+source = "registry+https://github.com/rust-lang/crates.io-index"
3741+checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
3742+
3743+[[package]]
31763744 name = "slab"
31773745 version = "0.4.12"
31783746 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3304,7 +3872,7 @@ checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b"
33043872 dependencies = [
33053873  "dotenvy",
33063874  "either",
3307- "heck",
3875+ "heck 0.5.0",
33083876  "hex",
33093877  "once_cell",
33103878  "proc-macro2",
@@ -3447,12 +4015,30 @@ dependencies = [
34474015 ]
34484016 
34494017 [[package]]
4018+name = "static_assertions"
4019+version = "1.1.0"
4020+source = "registry+https://github.com/rust-lang/crates.io-index"
4021+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
4022+
4023+[[package]]
34504024 name = "streaming-iterator"
34514025 version = "0.1.9"
34524026 source = "registry+https://github.com/rust-lang/crates.io-index"
34534027 checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520"
34544028 
34554029 [[package]]
4030+name = "string_cache"
4031+version = "0.8.9"
4032+source = "registry+https://github.com/rust-lang/crates.io-index"
4033+checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f"
4034+dependencies = [
4035+ "new_debug_unreachable",
4036+ "parking_lot",
4037+ "phf_shared",
4038+ "precomputed-hash",
4039+]
4040+
4041+[[package]]
34564042 name = "stringprep"
34574043 version = "0.1.5"
34584044 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3464,6 +4050,21 @@ dependencies = [
34644050 ]
34654051 
34664052 [[package]]
4053+name = "strip-ansi-escapes"
4054+version = "0.2.1"
4055+source = "registry+https://github.com/rust-lang/crates.io-index"
4056+checksum = "2a8f8038e7e7969abb3f1b7c2a811225e9296da208539e0f79c5251d6cac0025"
4057+dependencies = [
4058+ "vte",
4059+]
4060+
4061+[[package]]
4062+name = "strsim"
4063+version = "0.11.1"
4064+source = "registry+https://github.com/rust-lang/crates.io-index"
4065+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
4066+
4067+[[package]]
34674068 name = "subtle"
34684069 version = "2.6.1"
34694070 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3562,6 +4163,24 @@ dependencies = [
35624163 ]
35634164 
35644165 [[package]]
4166+name = "term"
4167+version = "1.2.1"
4168+source = "registry+https://github.com/rust-lang/crates.io-index"
4169+checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1"
4170+dependencies = [
4171+ "windows-sys 0.61.2",
4172+]
4173+
4174+[[package]]
4175+name = "termcolor"
4176+version = "1.4.1"
4177+source = "registry+https://github.com/rust-lang/crates.io-index"
4178+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
4179+dependencies = [
4180+ "winapi-util",
4181+]
4182+
4183+[[package]]
35654184 name = "terminal_size"
35664185 version = "0.4.4"
35674186 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3774,9 +4393,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
37744393 checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
37754394 dependencies = [
37764395  "serde",
3777- "serde_spanned",
3778- "toml_datetime",
3779- "toml_edit",
4396+ "serde_spanned 0.6.9",
4397+ "toml_datetime 0.6.11",
4398+ "toml_edit 0.22.27",
4399+]
4400+
4401+[[package]]
4402+name = "toml"
4403+version = "0.9.12+spec-1.1.0"
4404+source = "registry+https://github.com/rust-lang/crates.io-index"
4405+checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863"
4406+dependencies = [
4407+ "indexmap",
4408+ "serde_core",
4409+ "serde_spanned 1.1.1",
4410+ "toml_datetime 0.7.5+spec-1.1.0",
4411+ "toml_parser",
4412+ "toml_writer",
4413+ "winnow 0.7.15",
37804414 ]
37814415 
37824416 [[package]]
@@ -3789,6 +4423,15 @@ dependencies = [
37894423 ]
37904424 
37914425 [[package]]
4426+name = "toml_datetime"
4427+version = "0.7.5+spec-1.1.0"
4428+source = "registry+https://github.com/rust-lang/crates.io-index"
4429+checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
4430+dependencies = [
4431+ "serde_core",
4432+]
4433+
4434+[[package]]
37924435 name = "toml_edit"
37934436 version = "0.22.27"
37944437 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3796,10 +4439,32 @@ checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
37964439 dependencies = [
37974440  "indexmap",
37984441  "serde",
3799- "serde_spanned",
3800- "toml_datetime",
4442+ "serde_spanned 0.6.9",
4443+ "toml_datetime 0.6.11",
38014444  "toml_write",
3802- "winnow",
4445+ "winnow 0.7.15",
4446+]
4447+
4448+[[package]]
4449+name = "toml_edit"
4450+version = "0.23.10+spec-1.0.0"
4451+source = "registry+https://github.com/rust-lang/crates.io-index"
4452+checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269"
4453+dependencies = [
4454+ "indexmap",
4455+ "toml_datetime 0.7.5+spec-1.1.0",
4456+ "toml_parser",
4457+ "toml_writer",
4458+ "winnow 0.7.15",
4459+]
4460+
4461+[[package]]
4462+name = "toml_parser"
4463+version = "1.1.1+spec-1.1.0"
4464+source = "registry+https://github.com/rust-lang/crates.io-index"
4465+checksum = "39ca317ebc49f06bd748bfba29533eac9485569dc9bf80b849024b025e814fb9"
4466+dependencies = [
4467+ "winnow 1.0.1",
38034468 ]
38044469 
38054470 [[package]]
@@ -3809,6 +4474,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
38094474 checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
38104475 
38114476 [[package]]
4477+name = "toml_writer"
4478+version = "1.1.1+spec-1.1.0"
4479+source = "registry+https://github.com/rust-lang/crates.io-index"
4480+checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db"
4481+
4482+[[package]]
38124483 name = "tower"
38134484 version = "0.5.3"
38144485 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3980,6 +4651,12 @@ dependencies = [
39804651 ]
39814652 
39824653 [[package]]
4654+name = "typed-arena"
4655+version = "2.0.2"
4656+source = "registry+https://github.com/rust-lang/crates.io-index"
4657+checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
4658+
4659+[[package]]
39834660 name = "typenum"
39844661 version = "1.19.0"
39854662 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4049,6 +4726,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
40494726 checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
40504727 
40514728 [[package]]
4729+name = "unsafe-libyaml"
4730+version = "0.2.11"
4731+source = "registry+https://github.com/rust-lang/crates.io-index"
4732+checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
4733+
4734+[[package]]
40524735 name = "untrusted"
40534736 version = "0.9.0"
40544737 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4126,6 +4809,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
41264809 checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
41274810 
41284811 [[package]]
4812+name = "vte"
4813+version = "0.14.1"
4814+source = "registry+https://github.com/rust-lang/crates.io-index"
4815+checksum = "231fdcd7ef3037e8330d8e17e61011a2c244126acc0a982f4040ac3f9f0bc077"
4816+dependencies = [
4817+ "memchr",
4818+]
4819+
4820+[[package]]
4821+name = "walkdir"
4822+version = "2.5.0"
4823+source = "registry+https://github.com/rust-lang/crates.io-index"
4824+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
4825+dependencies = [
4826+ "same-file",
4827+ "winapi-util",
4828+]
4829+
4830+[[package]]
41294831 name = "want"
41304832 version = "0.3.1"
41314833 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4302,6 +5004,25 @@ dependencies = [
43025004 ]
43035005 
43045006 [[package]]
5007+name = "wide"
5008+version = "0.7.33"
5009+source = "registry+https://github.com/rust-lang/crates.io-index"
5010+checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03"
5011+dependencies = [
5012+ "bytemuck",
5013+ "safe_arch",
5014+]
5015+
5016+[[package]]
5017+name = "winapi-util"
5018+version = "0.1.11"
5019+source = "registry+https://github.com/rust-lang/crates.io-index"
5020+checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
5021+dependencies = [
5022+ "windows-sys 0.61.2",
5023+]
5024+
5025+[[package]]
43055026 name = "windows-core"
43065027 version = "0.62.2"
43075028 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4612,6 +5333,12 @@ dependencies = [
46125333 ]
46135334 
46145335 [[package]]
5336+name = "winnow"
5337+version = "1.0.1"
5338+source = "registry+https://github.com/rust-lang/crates.io-index"
5339+checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5"
5340+
5341+[[package]]
46155342 name = "wit-bindgen"
46165343 version = "0.51.0"
46175344 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4627,7 +5354,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
46275354 checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
46285355 dependencies = [
46295356  "anyhow",
4630- "heck",
5357+ "heck 0.5.0",
46315358  "wit-parser",
46325359 ]
46335360 
@@ -4638,7 +5365,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
46385365 checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
46395366 dependencies = [
46405367  "anyhow",
4641- "heck",
5368+ "heck 0.5.0",
46425369  "indexmap",
46435370  "prettyplease",
46445371  "syn",
@@ -4706,6 +5433,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
47065433 checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
47075434 
47085435 [[package]]
5436+name = "yaml_serde"
5437+version = "0.10.4"
5438+source = "registry+https://github.com/rust-lang/crates.io-index"
5439+checksum = "08c7c1b1a6a7c8a6b2741a6c21a4f8918e51899b111cfa08d1288202656e3975"
5440+dependencies = [
5441+ "indexmap",
5442+ "itoa",
5443+ "libyaml-rs",
5444+ "ryu",
5445+ "serde",
5446+]
5447+
5448+[[package]]
5449+name = "yansi"
5450+version = "1.0.1"
5451+source = "registry+https://github.com/rust-lang/crates.io-index"
5452+checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
5453+
5454+[[package]]
47095455 name = "yoke"
47105456 version = "0.8.1"
47115457 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -13,25 +13,25 @@ license = "AGPL-3.0-or-later"
1313 repository = "https://github.com/cospan-dev/cospan"
1414 
1515 [workspace.dependencies]
16-# panproto — all from git because panproto-grammars needs vendored C sources
17-# (too large for crates.io). All crates must come from the same source to avoid
18-# type mismatches between git and crates.io versions of panproto-schema etc.
19-panproto-core = { git = "https://github.com/panproto/panproto.git", tag = "v0.23.0" }
20-panproto-vcs = { git = "https://github.com/panproto/panproto.git", tag = "v0.23.0" }
21-panproto-schema = { git = "https://github.com/panproto/panproto.git", tag = "v0.23.0" }
22-panproto-check = { git = "https://github.com/panproto/panproto.git", tag = "v0.23.0" }
23-panproto-lens = { git = "https://github.com/panproto/panproto.git", tag = "v0.23.0" }
24-panproto-protocols = { git = "https://github.com/panproto/panproto.git", tag = "v0.23.0" }
25-panproto-io = { git = "https://github.com/panproto/panproto.git", tag = "v0.23.0" }
26-panproto-inst = { git = "https://github.com/panproto/panproto.git", tag = "v0.23.0" }
27-panproto-gat = { git = "https://github.com/panproto/panproto.git", tag = "v0.23.0" }
28-panproto-mig = { git = "https://github.com/panproto/panproto.git", tag = "v0.23.0" }
29-panproto-expr = { git = "https://github.com/panproto/panproto.git", tag = "v0.23.0" }
30-panproto-expr-parser = { git = "https://github.com/panproto/panproto.git", tag = "v0.23.0" }
31-panproto-xrpc = { git = "https://github.com/panproto/panproto.git", tag = "v0.23.0" }
32-panproto-parse = { git = "https://github.com/panproto/panproto.git", tag = "v0.23.0", features = ["group-all"] }
33-panproto-git = { git = "https://github.com/panproto/panproto.git", tag = "v0.23.0" }
34-panproto-project = { git = "https://github.com/panproto/panproto.git", tag = "v0.23.0" }
16+# panproto — path dependencies to local phrom checkout for development.
17+# Switch to git tags or crates.io versions for release builds.
18+panproto-core = { path = "/Users/awhite48/Projects/phrom/crates/panproto-core" }
19+panproto-vcs = { path = "/Users/awhite48/Projects/phrom/crates/panproto-vcs" }
20+panproto-schema = { path = "/Users/awhite48/Projects/phrom/crates/panproto-schema" }
21+panproto-check = { path = "/Users/awhite48/Projects/phrom/crates/panproto-check" }
22+panproto-lens = { path = "/Users/awhite48/Projects/phrom/crates/panproto-lens" }
23+panproto-lens-dsl = { path = "/Users/awhite48/Projects/phrom/crates/panproto-lens-dsl" }
24+panproto-protocols = { path = "/Users/awhite48/Projects/phrom/crates/panproto-protocols" }
25+panproto-io = { path = "/Users/awhite48/Projects/phrom/crates/panproto-io" }
26+panproto-inst = { path = "/Users/awhite48/Projects/phrom/crates/panproto-inst" }
27+panproto-gat = { path = "/Users/awhite48/Projects/phrom/crates/panproto-gat" }
28+panproto-mig = { path = "/Users/awhite48/Projects/phrom/crates/panproto-mig" }
29+panproto-expr = { path = "/Users/awhite48/Projects/phrom/crates/panproto-expr" }
30+panproto-expr-parser = { path = "/Users/awhite48/Projects/phrom/crates/panproto-expr-parser" }
31+panproto-xrpc = { path = "/Users/awhite48/Projects/phrom/crates/panproto-xrpc" }
32+panproto-parse = { path = "/Users/awhite48/Projects/phrom/crates/panproto-parse", features = ["group-all"] }
33+panproto-git = { path = "/Users/awhite48/Projects/phrom/crates/panproto-git" }
34+panproto-project = { path = "/Users/awhite48/Projects/phrom/crates/panproto-project" }
3535 git2 = "0.20"
3636 
3737 # Web framework
@@ -19,7 +19,7 @@ panproto-mig.workspace = true
1919 panproto-inst.workspace = true
2020 panproto-lens.workspace = true
2121 panproto-expr.workspace = true
22-panproto-expr-parser.workspace = true
22+panproto-lens-dsl.workspace = true
2323 panproto-check.workspace = true
2424 panproto-gat.workspace = true
2525 
@@ -20,10 +20,8 @@ pub fn emit_all_views_from_lenses(
2020 
2121     for lens in crate::lens_config::db_projection_lenses(lenses) {
2222         if let Some((schema, _)) = schemas.iter().find(|(_, nsid)| nsid == &lens.source) {
23-            let body_id = find_record_body(schema, &lens.source);
24-            let chain = crate::lens_config::steps_to_protolens_chain(&lens.steps, &body_id);
2523             let protocol = Protocol::default();
26-            let target = match chain.instantiate(schema, &protocol) {
24+            let target = match lens.chain.instantiate(schema, &protocol) {
2725                 Ok(l) => l.tgt_schema,
2826                 Err(e) => {
2927                     eprintln!("  warn: lens instantiation for {}: {e:?}", lens.source);
@@ -31,8 +29,8 @@ pub fn emit_all_views_from_lenses(
3129                 }
3230             };
3331 
34-            let table = lens.table.as_ref();
35-            let view_name = table
32+            let table = crate::lens_config::table_config(lens);
33+            let view_name = table.as_ref()
3634                 .map(|t| {
3735                     t.row_struct
3836                         .strip_suffix("Row")
@@ -41,10 +39,10 @@ pub fn emit_all_views_from_lenses(
4139                         + "View"
4240                 })
4341                 .unwrap_or_else(|| nsid_to_pascal(&lens.source) + "View");
44-            let include_did = table.map(|t| t.include_did).unwrap_or(true);
45-            let include_rkey = table.map(|t| t.include_rkey).unwrap_or(true);
42+            let include_did = table.as_ref().map(|t| t.include_did).unwrap_or(true);
43+            let include_rkey = table.as_ref().map(|t| t.include_rkey).unwrap_or(true);
4644             let empty_defaults = std::collections::HashMap::new();
47-            let column_defaults = table
45+            let column_defaults = table.as_ref()
4846                 .map(|t| &t.column_defaults)
4947                 .unwrap_or(&empty_defaults);
5048 
@@ -1,83 +1,66 @@
1-//! Load human-readable lens JSON files and convert to panproto ProtolensChain.
1+//! Load lens files via panproto-lens-dsl.
22 //!
3-//! Lens files use a concise DSL:
4-//!   { "remove_field": "fieldName" }           → combinators::remove_field
5-//!   { "rename_field": { "old": "x", "new": "y" } } → combinators::rename_field
6-//!   { "add_field": { "name": "x", "kind": "string", "default": "" } } → combinators::add_field
7-//!   { "add_field": { ..., "expr": "head(split(...))" } } → add_field + ComputeField
8-//!   { "apply_expr": { "field": "x", "expr": "match(...)" } } → ApplyExpr
9-//!   { "compute_field": { "target": "x", "expr": "concat(...)" } } → ComputeField
10-//!
11-//! Schema-level steps (remove, rename, add) → ProtolensChain via panproto combinators.
12-//! Value-level steps (expr, compute) → FieldTransforms via panproto expression parser.
13-//! Both come from the same JSON file; they're applied at different levels.
3+//! Thin wrapper around panproto_lens_dsl for cospan-specific conventions
4+//! (db-projection vs interop lens IDs, table metadata in extensions).
145 
156 use std::collections::HashMap;
167 use std::path::Path;
178 
189 use anyhow::{Context, Result};
19-use panproto_gat::{CoercionClass, Name};
20-use panproto_inst::value::Value;
21-use panproto_inst::FieldTransform;
22-use panproto_lens::{combinators, ProtolensChain};
23-use serde::Deserialize;
10+use panproto_lens_dsl::CompiledLens;
2411 
25-/// A human-readable lens file.
26-#[derive(Debug, Deserialize)]
27-pub struct LensFile {
28-    #[serde(rename = "$type")]
29-    pub type_id: String,
30-    pub id: String,
31-    #[serde(default)]
32-    pub description: String,
33-    pub source: String,
34-    pub target: String,
35-    pub steps: Vec<LensStep>,
36-    #[serde(default)]
37-    pub table: Option<TableConfig>,
38-}
12+/// Type alias for backward compatibility.
13+pub type LensFile = CompiledLens;
14+
15+/// Load and compile all lens files from a directory.
16+pub fn load_all_lenses(lenses_dir: &Path) -> Result<Vec<CompiledLens>> {
17+    if !lenses_dir.exists() {
18+        return Ok(Vec::new());
19+    }
20+
21+    let result = panproto_lens_dsl::load_dir(lenses_dir)
22+        .with_context(|| format!("loading lenses from {}", lenses_dir.display()))?;
23+
24+    for (path, err) in &result.errors {
25+        eprintln!("  warn: failed to load {}: {err}", path.display());
26+    }
27+
28+    let resolver = |_id: &str| -> Option<CompiledLens> { None };
29+    let mut compiled = Vec::new();
30+
31+    for doc in &result.documents {
32+        let body_vertex = format!("{}:body", doc.source);
33+        match panproto_lens_dsl::compile(doc, &body_vertex, &resolver) {
34+            Ok(c) => compiled.push(c),
35+            Err(e) => eprintln!("  warn: compile lens {}: {e}", doc.id),
36+        }
37+    }
3938 
40-/// A single step in the lens pipeline.
41-#[derive(Debug, Deserialize)]
42-#[serde(untagged)]
43-pub enum LensStep {
44-    RemoveField { remove_field: String },
45-    RenameField { rename_field: RenameSpec },
46-    AddField { add_field: AddFieldSpec },
47-    ApplyExpr { apply_expr: ApplyExprSpec },
48-    ComputeField { compute_field: ComputeFieldSpec },
39+    Ok(compiled)
4940 }
5041 
51-#[derive(Debug, Deserialize)]
52-pub struct RenameSpec {
53-    pub old: String,
54-    pub new: String,
42+/// DB projection lenses (id ending in ".db-projection").
43+pub fn db_projection_lenses(lenses: &[CompiledLens]) -> Vec<&CompiledLens> {
44+    lenses.iter().filter(|l| l.id.ends_with(".db-projection")).collect()
5545 }
5646 
57-#[derive(Debug, Deserialize)]
58-pub struct AddFieldSpec {
59-    pub name: String,
60-    pub kind: String,
61-    #[serde(default)]
62-    pub default: serde_json::Value,
63-    #[serde(default)]
64-    pub expr: Option<String>,
47+/// Interop lenses (id ending in ".interop").
48+pub fn interop_lenses(lenses: &[CompiledLens]) -> Vec<&CompiledLens> {
49+    lenses.iter().filter(|l| l.id.ends_with(".interop")).collect()
6550 }
6651 
67-#[derive(Debug, Deserialize)]
68-pub struct ApplyExprSpec {
69-    pub field: String,
70-    pub expr: String,
52+/// Find a compiled lens by source NSID.
53+pub fn find_by_source<'a>(lenses: &'a [CompiledLens], source: &str) -> Option<&'a CompiledLens> {
54+    lenses.iter().find(|l| l.source == source)
7155 }
7256 
73-#[derive(Debug, Deserialize)]
74-pub struct ComputeFieldSpec {
75-    pub target: String,
76-    pub expr: String,
57+/// Extract table config from the lens's extensions.
58+pub fn table_config(lens: &CompiledLens) -> Option<TableConfig> {
59+    lens.extensions.get("table").and_then(|v| serde_json::from_value(v.clone()).ok())
7760 }
7861 
79-/// DDL metadata (optional, only for db-projection lenses).
80-#[derive(Debug, Deserialize)]
62+/// DDL metadata from the "table" extension.
63+#[derive(Debug, serde::Deserialize)]
8164 pub struct TableConfig {
8265     pub name: String,
8366     pub row_struct: String,
@@ -89,180 +72,9 @@ pub struct TableConfig {
8972     #[serde(default = "default_true")]
9073     pub include_rkey: bool,
9174     #[serde(default)]
92-    pub indexes: Vec<serde_json::Value>,
93-    #[serde(default)]
94-    pub foreign_keys: Vec<serde_json::Value>,
95-    #[serde(default)]
9675     pub column_defaults: HashMap<String, String>,
9776     #[serde(default)]
9877     pub counter_fields: Vec<String>,
9978 }
10079 
10180 fn default_true() -> bool { true }
102-
103-/// Load all lens files from a directory.
104-pub fn load_all_lenses(lenses_dir: &Path) -> Result<Vec<LensFile>> {
105-    let mut lenses = Vec::new();
106-    if !lenses_dir.exists() {
107-        return Ok(lenses);
108-    }
109-    for entry in std::fs::read_dir(lenses_dir)? {
110-        let path = entry?.path();
111-        if path.extension().and_then(|e| e.to_str()) == Some("json") {
112-            let json_str = std::fs::read_to_string(&path)
113-                .with_context(|| format!("reading {}", path.display()))?;
114-            let lens: LensFile = serde_json::from_str(&json_str)
115-                .with_context(|| format!("parsing {}", path.display()))?;
116-            lenses.push(lens);
117-        }
118-    }
119-    Ok(lenses)
120-}
121-
122-/// Convert schema-level steps to a ProtolensChain using panproto combinators.
123-pub fn steps_to_protolens_chain(steps: &[LensStep], body_id: &str) -> ProtolensChain {
124-    let mut chains: Vec<ProtolensChain> = Vec::new();
125-
126-    for step in steps {
127-        match step {
128-            LensStep::RemoveField { remove_field } => {
129-                let vertex_id = format!("{body_id}.{remove_field}");
130-                chains.push(combinators::remove_field(vertex_id));
131-            }
132-            LensStep::RenameField { rename_field } => {
133-                let field_vertex = format!("{body_id}.{}", rename_field.old);
134-                chains.push(combinators::rename_field(
135-                    body_id,
136-                    &*field_vertex,
137-                    &*rename_field.old,
138-                    &*rename_field.new,
139-                ));
140-            }
141-            LensStep::AddField { add_field } => {
142-                let vertex_id = format!("{body_id}.{}", add_field.name);
143-                let default = json_to_value(&add_field.default, &add_field.kind);
144-                chains.push(combinators::add_field(
145-                    body_id,
146-                    &*vertex_id,
147-                    &*add_field.kind,
148-                    default,
149-                ));
150-            }
151-            // Expression steps are value-level, not schema-level — handled by
152-            // steps_to_value_transforms, not by protolens combinators.
153-            LensStep::ApplyExpr { .. } | LensStep::ComputeField { .. } => {}
154-        }
155-    }
156-
157-    combinators::pipeline(chains)
158-}
159-
160-/// Extract value-level transforms (expressions) from lens steps.
161-///
162-/// These are injected into the CompiledMigration's field_transforms after
163-/// the schema-level chain is instantiated. Uses panproto_expr_parser to
164-/// parse expression strings from the lens JSON.
165-pub fn steps_to_value_transforms(
166-    steps: &[LensStep],
167-    body_vertex: &str,
168-) -> HashMap<Name, Vec<FieldTransform>> {
169-    let mut transforms: HashMap<Name, Vec<FieldTransform>> = HashMap::new();
170-    let key = Name::from(body_vertex);
171-
172-    for step in steps {
173-        match step {
174-            LensStep::AddField { add_field } if add_field.expr.is_some() => {
175-                let expr_str = add_field.expr.as_ref().unwrap();
176-                if let Some(expr) = parse_expr(expr_str) {
177-                    transforms.entry(key.clone()).or_default().push(
178-                        FieldTransform::ComputeField {
179-                            target_key: add_field.name.clone(),
180-                            expr,
181-                            inverse: None,
182-                            coercion_class: CoercionClass::Projection,
183-                        },
184-                    );
185-                }
186-            }
187-            LensStep::ApplyExpr { apply_expr } => {
188-                if let Some(expr) = parse_expr(&apply_expr.expr) {
189-                    transforms.entry(key.clone()).or_default().push(
190-                        FieldTransform::ApplyExpr {
191-                            key: apply_expr.field.clone(),
192-                            expr,
193-                            inverse: None,
194-                            coercion_class: CoercionClass::Projection,
195-                        },
196-                    );
197-                }
198-            }
199-            LensStep::ComputeField { compute_field } => {
200-                if let Some(expr) = parse_expr(&compute_field.expr) {
201-                    transforms.entry(key.clone()).or_default().push(
202-                        FieldTransform::ComputeField {
203-                            target_key: compute_field.target.clone(),
204-                            expr,
205-                            inverse: None,
206-                            coercion_class: CoercionClass::Projection,
207-                        },
208-                    );
209-                }
210-            }
211-            _ => {}
212-        }
213-    }
214-
215-    transforms
216-}
217-
218-/// Parse a panproto expression string using panproto_expr_parser.
219-fn parse_expr(expr_str: &str) -> Option<panproto_expr::Expr> {
220-    let tokens = match panproto_expr_parser::tokenize(expr_str) {
221-        Ok(t) => t,
222-        Err(e) => {
223-            eprintln!("  warn: failed to tokenize expression '{expr_str}': {e}");
224-            return None;
225-        }
226-    };
227-    match panproto_expr_parser::parse(&tokens) {
228-        Ok(expr) => Some(expr),
229-        Err(errors) => {
230-            for e in &errors {
231-                eprintln!("  warn: parse error in expression '{expr_str}': {e}");
232-            }
233-            None
234-        }
235-    }
236-}
237-
238-/// Convert a JSON default value to a panproto Value.
239-fn json_to_value(json: &serde_json::Value, kind: &str) -> Value {
240-    match json {
241-        serde_json::Value::Null => Value::Null,
242-        serde_json::Value::String(s) => Value::Str(s.clone()),
243-        serde_json::Value::Number(n) => {
244-            if let Some(i) = n.as_i64() { Value::Int(i) }
245-            else if let Some(f) = n.as_f64() { Value::Float(f) }
246-            else { Value::Int(0) }
247-        }
248-        serde_json::Value::Bool(b) => Value::Bool(*b),
249-        _ => match kind {
250-            "integer" => Value::Int(0),
251-            "number" | "float" => Value::Float(0.0),
252-            "boolean" => Value::Bool(false),
253-            _ => Value::Str(String::new()),
254-        },
255-    }
256-}
257-
258-pub fn db_projection_lenses(lenses: &[LensFile]) -> Vec<&LensFile> {
259-    lenses.iter().filter(|l| l.id.ends_with(".db-projection")).collect()
260-}
261-
262-pub fn interop_lenses(lenses: &[LensFile]) -> Vec<&LensFile> {
263-    lenses.iter().filter(|l| l.id.ends_with(".interop")).collect()
264-}
265-
266-pub fn find_by_source<'a>(lenses: &'a [LensFile], source_nsid: &str) -> Option<&'a LensFile> {
267-    lenses.iter().find(|l| l.source == source_nsid)
268-}
cospan · schematic version control on atproto built on AT Protocol