Files
roto/TASKS.md
T
2026-06-30 00:49:03 -04:00

123 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Task: Verify Generated Code Works in `no_std` / `no_alloc`
**Goal:** Ensure all generated protobuf code compiles and runs in `no_std` + `no_alloc` environments. Features requiring `alloc` must be gated behind feature flags in the proto generator's output.
---
## Findings
**Already clean (no_std + no_alloc):**
- All reader structs (`Message<'a>`) — zero-copy accessors over `&[u8]`
- All field accessors, enums, builders (`MessageBuilder<'b>`), typed iterators
- Core runtime: `ProtoAccessor`, `ProtoBuilder`, `RevBuilder`, `read_varint`, `write_varint`, all iterators
**Needs `alloc` feature gate:**
- `RotoMessage` trait — uses `bytes::Bytes` (has `Arc` internally)
- `RotoOwned` trait — same issue
- `OwnedMessage` structs generated by codegen
- `RevBuilder::with()` — uses `Vec<_>` to collect raw fields
- `Bytes`, `BytesMut` imports in `DATA_IMPORTS`
**Needs `std` feature gate:**
- All service code (`SERVICE_IMPORTS`) — uses `std::sync::Arc`, `std::pin::Pin`, `String`, etc.
- `std::error::Error for RotoError` (already gated)
---
## Plan
### Phase 1: Runtime (`runtime/src/lib.rs`, `runtime/Cargo.toml`)
1. **Gate `RotoMessage` and `RotoOwned`** behind `#[cfg(feature = "alloc")]`
2. **Gate `BufMutBuilder`** behind `#[cfg(feature = "alloc")]` — only useful with `BytesMut`, which requires alloc
3. **Gate `use bytes::BufMut`** — only import it conditionally
4. **Wire up `bytes` features in Cargo.toml:**
```toml
bytes = { version = "1.7", default-features = false }
# ...
[features]
default = ["std", "alloc"]
std = ["bytes/std"]
alloc = ["bytes"]
```
### Phase 2: Codegen — `DATA_IMPORTS` & Generated Data Code (`codegen/src/generator/mod.rs`)
5. **Split `DATA_IMPORTS`** into unconditional + gated sections:
```rust
// Always emitted
use roto_runtime::{ProtoAccessor, ProtoBuilder, RevBuilder, Result, RotoError, read_varint, RepeatedFieldIterator};
use core::str;
use bytes::{Buf, BufMut};
// Only emitted when alloc feature is enabled
#[cfg(feature = "alloc")]
use bytes::{Bytes, BytesMut};
#[cfg(feature = "alloc")]
use roto_runtime::RotoMessage;
```
6. **Gate `OwnedMessage` structs + `RotoMessage` impls** with `#[cfg(feature = "alloc")]`
7. **Fix `RevBuilder::with()`** — replace `let fields: Vec<_> = ... .collect()` with a forward iteration that doesn't allocate. The reverse order doesn't matter for protobuf encoding (fields are independent):
```rust
// Before: let fields: Vec<_> = msg.accessor.raw_fields().collect();
// for (raw, _) in fields.iter().rev() { ... }
// After: for (raw, _) in msg.accessor.raw_fields() { ... }
```
Alternatively, gate the `with()` method itself behind `#[cfg(feature = "alloc")]`.
### Phase 3: Codegen — Service Code (`codegen/src/generator/mod.rs`)
8. **Gate `SERVICE_IMPORTS`** with `#[cfg(feature = "std")]`
9. **Gate all service traits, server impls, and `*Server` structs** with `#[cfg(feature = "std")]`
### Phase 4: Generated Consumer Setup
10. **Emit a `Cargo.toml`** (or instruct consumers) with the right feature setup:
```toml
[features]
default = ["std"]
std = ["roto-runtime/std", "alloc"]
alloc = ["roto-runtime/alloc", "bytes"]
```
### Phase 5: Validation
11. **Update `examples/no_std_test`** to build with `--no-default-features` and verify compilation
12. **Run existing tests** with default features to ensure nothing breaks
13. **Add a compile-test** that verifies `--no-default-features` builds succeed for generated code
---
## Dependency Graph
```
no_std + no_alloc → readers, enums, builders, iterators
↓ alloc → + OwnedMessage, RotoMessage trait
↓ std → + service code, std::error::Error
```
## Risk Assessment
| Risk | Mitigation |
|------|-----------|
| Breaking existing consumers who use `OwnedMessage` | `OwnedMessage` was already behind a manual `#[cfg]` in the `no_std_test` example — consumers aren't relying on it unconditionally |
| `BufMutBuilder` removal in no-alloc mode | It's only useful with `BytesMut` anyway, which requires alloc |
| `RevBuilder::with()` change | Forward iteration is semantically equivalent for protobuf encoding |
## Files to Modify
| File | Changes |
|------|---------|
| `runtime/src/lib.rs` | Gate `RotoMessage`, `RotoOwned`, `BufMutBuilder`, `use bytes::BufMut` |
| `runtime/Cargo.toml` | Wire `alloc` → `bytes` dependency, add `bytes/std` to `std` feature |
| `codegen/src/generator/mod.rs` | Split `DATA_IMPORTS`, gate `OwnedMessage`, gate service code, fix `RevBuilder::with()` |
| `examples/no_std_test/` | Update to use new feature structure |
## Execution Order
1. Phase 1 (runtime) — foundation
2. Phase 2 (codegen data imports) — depends on runtime
3. Phase 3 (codegen services) — independent
4. Phase 45 (consumer setup + validation) — depends on all above