Extract gRPC service and method from a callee name. * Handles patterns like: pb.NewFooServiceClient(conn).GetBar → Foo/GetBar * Also: FooServiceGrpc.newBlockingStub(ch).getBar → FooService/getBar */
| 1805 | * Handles patterns like: pb.NewFooServiceClient(conn).GetBar → Foo/GetBar |
| 1806 | * Also: FooServiceGrpc.newBlockingStub(ch).getBar → FooService/getBar */ |
| 1807 | bool extract_grpc_service_method(const char *callee, char *service, size_t srv_sz, char *method, |
| 1808 | size_t meth_sz) { |
| 1809 | service[0] = '\0'; |
| 1810 | method[0] = '\0'; |
| 1811 | if (!callee) { |
| 1812 | return false; |
| 1813 | } |
| 1814 | /* Find last dot to split service.Method */ |
| 1815 | const char *last_dot = strrchr(callee, '.'); |
| 1816 | if (!last_dot || !last_dot[SKIP_ONE]) { |
| 1817 | return false; |
| 1818 | } |
| 1819 | snprintf(method, meth_sz, "%s", last_dot + SKIP_ONE); |
| 1820 | |
| 1821 | /* Extract service name: everything before the last dot, stripped of prefixes/suffixes */ |
| 1822 | size_t prefix_len = (size_t)(last_dot - callee); |
| 1823 | char raw[CBM_SZ_256]; |
| 1824 | if (prefix_len >= sizeof(raw)) { |
| 1825 | prefix_len = sizeof(raw) - SKIP_ONE; |
| 1826 | } |
| 1827 | memcpy(raw, callee, prefix_len); |
| 1828 | raw[prefix_len] = '\0'; |
| 1829 | |
| 1830 | /* Strip common prefixes: pb.New, New, pb. */ |
| 1831 | const char *s = raw; |
| 1832 | if (strncmp(s, "pb.New", CBM_SZ_6) == 0) { |
| 1833 | s += CBM_SZ_6; |
| 1834 | } else if (strncmp(s, "pb.", CBM_SZ_3) == 0 || strncmp(s, "New", CBM_SZ_3) == 0) { |
| 1835 | s += CBM_SZ_3; |
| 1836 | } |
| 1837 | |
| 1838 | /* Strip the generated-stub/client suffix, preserving the canonical |
| 1839 | * proto-declared service name. The proto service is `<X>Service`; the |
| 1840 | * generated client is `<X>ServiceClient` / `<X>ServiceGrpc`, so we strip |
| 1841 | * only the trailing stub/client token (Client/Stub/Grpc/…), NOT "Service" |
| 1842 | * itself — stripping "ServiceClient" yielded `<X>` and broke cross-repo |
| 1843 | * matching against the `<X>Service` declared name (#294). Longest tokens |
| 1844 | * first so e.g. BlockingStub wins over Stub. |
| 1845 | * |
| 1846 | * A match also serves as the gRPC stub-type signal: we ONLY emit a Route |
| 1847 | * when a recognized suffix is actually present. Without this gate the |
| 1848 | * fallback turned ordinary receiver vars (`_provider.GetGroup`, |
| 1849 | * `_builder.AddSomeService`) into phantom `__grpc__provider/...` Routes |
| 1850 | * that correspond to no .proto anywhere (#294). */ |
| 1851 | snprintf(service, srv_sz, "%s", s); |
| 1852 | size_t slen = strlen(service); |
| 1853 | static const char *const suffixes[] = {"BlockingStub", "FutureStub", "AsyncStub", |
| 1854 | "AsyncClient", "Servicer", "Client", |
| 1855 | "Stub", "Grpc", NULL}; |
| 1856 | bool stripped = false; |
| 1857 | for (const char *const *sfx = suffixes; *sfx; sfx++) { |
| 1858 | size_t flen = strlen(*sfx); |
| 1859 | if (slen > flen && strcmp(service + slen - flen, *sfx) == 0) { |
| 1860 | service[slen - flen] = '\0'; |
| 1861 | stripped = true; |
| 1862 | break; |
| 1863 | } |
| 1864 | } |
no outgoing calls
no test coverage detected