Skip to content

Bind net-imap (Net::IMAP) into rbgo - #202

Closed
tannevaled wants to merge 1 commit into
mainfrom
feat/bind-net-imap
Closed

tannevaled wants to merge 1 commit into
mainfrom
feat/bind-net-imap

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Summary

Wire the pure-Go go-ruby-net-imap/net-imap command builder + response parser into the interpreter so require "net/imap" works with no .rb file — a live Net::IMAP session over an injected IO seam.

The library is deterministic (IMAP4rev1 command encoding + the response grammar) and never opens a socket — the transport is the host's job. Mirroring the redis / pg bindings, Net::IMAP.new takes an injected IO-like connection (connection: / conn: keyword, or a positional IO — a StringIO, a TCPSocket, or an OpenSSL::SSL::SSLSocket for TLS, all responding to #read/#write). rubyConn bridges it to the library Reader's line/literal callbacks.

Surface

net_imap.go (shell + command methods) + net_imap_bind.go (protocol driver + value mapping + coercion):

  • Session: new, greeting, responses, disconnect, disconnected?
  • Commands: login, authenticate (PLAIN/LOGIN/CRAM-MD5/XOAUTH2 via the library's SASL encoders), select, examine, create, delete, rename, subscribe, unsubscribe, list, lsub, status, fetch, uid_fetch, store, uid_store, copy, uid_copy, search, uid_search, expunge, append (with literal continuation), capability, noop, logout, check, close, unselect, starttls
  • Module helpers: Net::IMAP.encode_utf7 / decode_utf7
  • Typed value classes mirroring Net::IMAP::* member-for-member: TaggedResponse / UntaggedResponse / ContinuationRequest / ResponseText / ResponseCode / MailboxList / StatusData / FetchData / Envelope / Address / AppendUIDData / CopyUIDData / BodyType{Text,Basic,Message,Multipart}
  • Error tree: Error / ResponseError / NoResponseError / BadResponseError / ByeResponseError / ResponseParseError / DataFormatError, with faithful NO/BAD/parse/transport mapping

Wiring

  • registerNetIMAP() in bootstrap after registerSocket / registerOpenSSL so a real TCPSocket/SSLSocket can be the TLS seam
  • providedFeatures gains "net/imap"
  • classOf gains *IMAPObj

Verification

  • Full suite internal/vm + internal/object green; -race on new tests green
  • 100% coverage of the new files (deterministic Ruby-level tests over an in-process duplex FakeSock — no real network, no leaked goroutines — plus Go-level unit tests for defensive arms)
  • gofmt -l clean, go vet clean, orphan-case scan clean
  • Builds on all six 64-bit arches (amd64/arm64/riscv64/loong64/ppc64le/s390x)

🤖 Generated with Claude Code

Wire the pure-Go go-ruby-net-imap/net-imap command builder + response
parser into the interpreter so `require "net/imap"` works with no .rb
file, giving a live Net::IMAP session over an injected IO seam.

The library is deterministic (command encoding + response grammar) and
never opens a socket; the transport is the host's job. Like the redis /
pg bindings, Net::IMAP.new takes an injected IO-like connection
(connection:/conn: keyword or a positional IO — a StringIO, a TCPSocket,
or an OpenSSL::SSL::SSLSocket for TLS, all responding to #read/#write).
rubyConn bridges it to the library Reader's line/literal callbacks.

Surface (net_imap.go + net_imap_bind.go):
  - Net::IMAP.new + greeting/responses/disconnect/disconnected?
  - login, authenticate (PLAIN/LOGIN/CRAM-MD5/XOAUTH2 via the library's
    SASL encoders), select, examine, create, delete, rename, subscribe,
    unsubscribe, list, lsub, status, fetch, uid_fetch, store, uid_store,
    copy, uid_copy, search, uid_search, expunge, append (literals),
    capability, noop, logout, check, close, unselect, starttls
  - Net::IMAP.encode_utf7 / decode_utf7
  - typed value classes mirroring Net::IMAP::* member-for-member
    (TaggedResponse / UntaggedResponse / ContinuationRequest /
    ResponseText / ResponseCode / MailboxList / StatusData / FetchData /
    Envelope / Address / AppendUIDData / CopyUIDData / BodyType{Text,
    Basic,Message,Multipart}) and the Net::IMAP error tree
  - full error mapping (NoResponseError / BadResponseError /
    ByeResponseError / ResponseParseError / DataFormatError)

registerNetIMAP runs after registerSocket/registerOpenSSL so a real
TCPSocket/SSLSocket can be the TLS seam; providedFeatures gains
"net/imap"; classOf gains *IMAPObj.

Tests drive the full surface over an in-process duplex FakeSock (no real
network, no leaked goroutines); 100% coverage of the new files
(deterministic Ruby-level tests + Go-level unit tests for defensive
arms). gofmt/vet/-race clean; builds on all six 64-bit arches.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@tannevaled

Copy link
Copy Markdown
Contributor Author

Superseded by the net-wave consolidation PR (single merge onto main; shared builtins/require/object_model). Work preserved verbatim; branch kept.

@tannevaled tannevaled closed this Jul 6, 2026
Sign up for free to 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.

1 participant