') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Replace Factory boilerplate with derive macros by npajkovsky · Pull Request #76 · bcgit/bc-rust · GitHub
Skip to content

Replace Factory boilerplate with derive macros - #76

Open
npajkovsky wants to merge 2 commits into
bcgit:release/0.1.3alphafrom
npajkovsky:proc-macro-hash
Open

Replace Factory boilerplate with derive macros#76
npajkovsky wants to merge 2 commits into
bcgit:release/0.1.3alphafrom
npajkovsky:proc-macro-hash

Conversation

@npajkovsky

Copy link
Copy Markdown
Collaborator

HashFactory hand-wrote an 8-arm match for each of the 10 Hash trait methods plus the string-name lookup in AlgorithmFactory::new, so adding an algorithm meant touching a dozen places. Add a bouncycastle-factory-macros crate providing two derives that generate all of it:

  • Hash - the Hash impl, forwarding every method to the
    variant currently held.
  • AlgorithmFactory - Default and AlgorithmFactory (new, default_128_bit,
    default_256_bit), driven by a #[factory(name = ...)]
    helper attribute on each variant.

Adding an algorithm is now one variant plus one attribute. hash_factory.rs drops from 254 to 79 lines with no behaviour change; existing tests and doctests cover the generated code.

Each derive is named after the trait it implements - derives live in the macro namespace and traits in the type namespace, so they don't collide (same convention as serde::Serialize).

Also marks HashFactory #[non_exhaustive] so future variants are additive rather than breaking for downstream matches

Fixes: #66

@npajkovskynpajkovsky self-assigned this Aug 13, 2026
@ounsworth

ounsworth commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

I didn't know about the #[non_exhaustive] attribute. That's great. Regardless of whether we macro the factories or not, we should apply that, and I will especially be adding #[non_exhaustive] to all the error type enums.

I particularly like that it doesn't prevent you, inside the crate, from writing exhaustive matches
for example:

 match self {
Self::SHAKE128(h) => h.hash_xof_out(data, output),
Self::SHAKE256(h) => h.hash_xof_out(data, output),
}

which will break and force you to add a new branch if you add a new type to the struct, but any caller outside the library would be required to add the _ = branch if they tried to do the same thing. Brilliant! Thanks for pointing that out.

I created: #79

HashFactory hand-wrote an 8-arm match for each of the 10 Hash trait
methods plus the string-name lookup in AlgorithmFactory::new, so adding
an algorithm meant touching a dozen places. Add a bouncycastle-factory-macros
crate providing two derives that generate all of it:
* Hash - the Hash impl, forwarding every method to the
variant currently held.
* AlgorithmFactory - Default and AlgorithmFactory (new, default_128_bit,
default_256_bit), driven by a #[factory(name = ...)]
helper attribute on each variant.
Adding an algorithm is now one variant plus one attribute. hash_factory.rs
drops from 254 to 79 lines with no behaviour change; existing tests and
doctests cover the generated code.
Each derive is named after the trait it implements - derives live in the
macro namespace and traits in the type namespace, so they don't collide
(same convention as serde::Serialize).
Also marks HashFactory #[non_exhaustive] so future variants are additive
rather than breaking for downstream matches
Fixes: bcgit#66
Signed-off-by: Nikola Pajkovsky <nikolap@openssl.org>
Signed-off-by: Nikola Pajkovsky <nikolap@openssl.org>
@npajkovsky

Copy link
Copy Markdown
CollaboratorAuthor

@jjkurczak@ounsworth I have pushed the KDF factory proc-macro.

@npajkovskynpajkovsky changed the title Replace HashFactory boilerplate with derive macrosReplace Factory boilerplate with derive macrosAug 26, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@npajkovsky@ounsworth@nikolapajkovsky