Skip to main content

qubit_codec/transcode/io/
transcode_encode_output.rs

1// =============================================================================
2//    Copyright (c) 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! Buffered output driver that encodes values into units.
9
10use core::fmt;
11use core::num::NonZeroUsize;
12use core::result::Result as CoreResult;
13use std::collections::TryReserveError;
14use std::io::Error;
15use std::io::ErrorKind;
16use std::io::Result;
17use std::io::Seek;
18use std::io::SeekFrom;
19use std::io::Write;
20
21use qubit_io::Buffer;
22use qubit_io::BufferedOutput;
23use qubit_io::Output;
24use qubit_io::Seekable;
25use qubit_utils::SliceRange;
26use qubit_utils::allocation_error;
27
28use super::transcode_progress_validation::validate_encode_progress;
29use crate::CapacityError;
30use crate::Codec;
31use crate::TranscodeEncodeError;
32use crate::TranscodeEncodeErrorOf;
33use crate::TranscodeEncoder;
34use crate::TranscodeStatus;
35use crate::Transcoder;
36use crate::value::codec_value_lifecycle::encode_complete_value_into_reserved;
37use crate::value::codec_value_lifecycle::max_complete_encode_units;
38
39/// Encodes an [`Output`] value stream into an [`Output`] unit stream.
40///
41/// This type owns only the unit-level [`qubit_io::BufferedOutput`]. Callers
42/// pass a [`crate::Codec`] and error mapper to each encode operation, which
43/// lets one buffered output drive different encoders without nesting buffers or
44/// storing codec-specific state in the buffer owner.
45///
46/// [`Self::flush`] only drains already buffered units. State-aware streaming
47/// encoders can use [`Self::reset`], [`Self::transcode`], and [`Self::finish`]
48/// explicitly.
49///
50/// # Type Parameters
51///
52/// * `O` - Wrapped unit output.
53///
54/// # Examples
55///
56/// ```
57/// use std::io::Cursor;
58///
59/// use qubit_codec::TranscodeEncodeOutput;
60///
61/// let output = TranscodeEncodeOutput::with_capacity(Cursor::new(Vec::<u8>::new()), 8);
62/// assert!(output.spare_capacity() >= 8);
63/// ```
64pub struct TranscodeEncodeOutput<O>
65where
66    O: Output,
67    O::Item: Copy + Default,
68{
69    /// Owns the wrapped unit sink and pending encoded units awaiting delivery.
70    output: BufferedOutput<O>,
71}
72
73impl<O> TranscodeEncodeOutput<O>
74where
75    O: Output,
76    O::Item: Copy + Default,
77{
78    /// Creates an encoder output with the default unit buffer capacity.
79    ///
80    /// # Parameters
81    ///
82    /// * `inner` - Unit output written by this adapter.
83    ///
84    /// # Returns
85    ///
86    /// A new buffered encoder output.
87    #[must_use]
88    pub fn new(inner: O) -> Self {
89        Self {
90            output: BufferedOutput::new(inner),
91        }
92    }
93
94    /// Creates an encoder output with a unit buffer of at least `capacity`.
95    ///
96    /// # Parameters
97    ///
98    /// * `inner` - Unit output written by this adapter.
99    /// * `capacity` - Requested internal unit buffer capacity.
100    ///
101    /// # Returns
102    ///
103    /// A new buffered encoder output.
104    #[must_use]
105    pub fn with_capacity(inner: O, capacity: usize) -> Self {
106        Self {
107            output: BufferedOutput::with_capacity(inner, capacity),
108        }
109    }
110
111    /// Creates an encoder output with a unit buffer of at least `capacity`.
112    ///
113    /// # Parameters
114    ///
115    /// * `inner` - Unit output written by this adapter.
116    /// * `capacity` - Requested internal unit buffer capacity.
117    ///
118    /// # Errors
119    ///
120    /// Returns an allocation error when the requested buffer cannot be
121    /// allocated.
122    ///
123    /// # Returns
124    ///
125    /// A buffered adapter owning `inner` on success.
126    pub fn try_with_capacity(inner: O, capacity: usize) -> CoreResult<Self, TryReserveError> {
127        Ok(Self {
128            output: BufferedOutput::try_with_capacity(inner, capacity)?,
129        })
130    }
131
132    /// Returns a shared reference to the wrapped unit output.
133    ///
134    /// Pending units remain in this adapter's internal buffer and are not
135    /// visible through the returned output until [`Self::flush`] succeeds.
136    ///
137    /// # Returns
138    ///
139    /// A shared reference to the wrapped unit output.
140    #[must_use]
141    #[inline]
142    pub const fn inner(&self) -> &O {
143        self.output.inner()
144    }
145
146    /// Returns the available capacity of the spare output buffer.
147    ///
148    /// # Returns
149    ///
150    /// The number of output units that can still be appended without flushing.
151    #[must_use]
152    #[inline]
153    pub fn spare_capacity(&self) -> usize {
154        self.output.spare_capacity()
155    }
156
157    /// Returns raw spare-buffer parts for the internal output buffer.
158    ///
159    /// # Returns
160    ///
161    /// The full backing storage, the spare start index, and the spare unit
162    /// count.
163    #[must_use]
164    #[inline]
165    pub fn spare_raw_parts_mut(&mut self) -> (&mut [O::Item], usize, usize) {
166        self.output.spare_raw_parts_mut()
167    }
168
169    /// Marks `count` units from [`Self::spare_raw_parts_mut`] as written.
170    ///
171    /// # Parameters
172    ///
173    /// * `count` - Initialized units to commit from the spare window.
174    ///
175    /// # Safety
176    ///
177    /// The caller must guarantee that `count <= Self::spare_capacity()` and
178    /// that the corresponding units in the returned spare slice have been
179    /// initialized.
180    #[inline]
181    pub unsafe fn advance(&mut self, count: usize) {
182        // SAFETY: The caller guarantees `count` and initialization invariants.
183        unsafe { self.output.advance(count) }
184    }
185
186    /// Ensures that at least `count` spare units are available.
187    ///
188    /// # Parameters
189    ///
190    /// * `count` - Number of spare units required.
191    ///
192    /// # Errors
193    ///
194    /// Returns allocation errors mapped to [`ErrorKind::OutOfMemory`], or I/O
195    /// errors from the wrapped output while flushing pending units.
196    pub fn ensure_spare_capacity(&mut self, count: usize) -> Result<()> {
197        let pending = self.output.capacity() - self.output.spare_capacity();
198        let required_capacity = pending.saturating_add(count);
199        self.output
200            .try_reserve_capacity(required_capacity)
201            .map_err(allocation_error)?;
202        self.output.ensure_spare_capacity(count)
203    }
204
205    /// Consumes this adapter without flushing the wrapped output.
206    ///
207    /// This method does not call [`Self::flush`] and performs no I/O. Pending
208    /// units remain in the returned buffer, which transfers their delivery
209    /// responsibility to the caller.
210    ///
211    /// # Returns
212    ///
213    /// The wrapped output and the buffer holding pending units.
214    #[must_use = "the returned output and pending buffer must be handled"]
215    #[inline]
216    pub fn into_parts(self) -> (O, Buffer<O::Item>) {
217        self.output.into_parts()
218    }
219
220    /// Flushes buffered units without finishing any encoder stream.
221    ///
222    /// # Errors
223    ///
224    /// Returns errors from the wrapped output while flushing pending units.
225    pub fn flush(&mut self) -> Result<()> {
226        self.output.flush()
227    }
228
229    /// Encodes one codec value into this buffered unit output.
230    ///
231    /// The method grows the persistent internal buffer when necessary, then
232    /// writes the complete encoded value into its spare window.
233    ///
234    /// # Type Parameters
235    ///
236    /// * `C` - Codec producing the wrapped output unit type.
237    /// * `M` - Mapper from codec errors into I/O errors.
238    ///
239    /// # Parameters
240    ///
241    /// * `codec` - Codec used for this single-value encode.
242    /// * `value` - Value to encode.
243    /// * `map_error` - Mapper for codec-domain encode errors.
244    ///
245    /// # Errors
246    ///
247    /// Returns I/O errors from the wrapped output, `InvalidInput` when the
248    /// codec output bound overflows or the value is outside the codec domain,
249    /// or the error returned by `map_error` for codec encode, reset, and finish
250    /// failures.
251    ///
252    /// # Panics
253    ///
254    /// Panics when the codec violates its declared reset, value-width, or
255    /// finish bounds, or when `encode` writes a length different from
256    /// `encode_len` in the same reset state.
257    pub fn write_encoded_with<C, M>(&mut self, codec: &mut C, value: &C::Value, mut map_error: M) -> Result<()>
258    where
259        C: Codec<Unit = O::Item>,
260        M: FnMut(C::EncodeError) -> Error,
261    {
262        let max_units = map_encode_value_result(
263            max_complete_encode_units::<C>().map_err(TranscodeEncodeErrorOf::<C>::from),
264            &mut map_error,
265        )?;
266        self.ensure_spare_capacity(max_units)?;
267        let (units, output_index, available) = self.output.spare_raw_parts_mut();
268        debug_assert!(
269            available >= max_units,
270            "reserved spare buffer is smaller than codec upper bound",
271        );
272        let written = map_encode_value_result(
273            encode_complete_value_into_reserved(codec, value, units, output_index, max_units),
274            &mut map_error,
275        )?;
276        // SAFETY: The spare buffer has the conservative complete lifecycle
277        // capacity, and the helper reports how many initialized units it
278        // actually wrote.
279        unsafe {
280            self.output.advance(written);
281        }
282        Ok(())
283    }
284
285    /// Runs encoder reset and buffers any stream-prefix units.
286    ///
287    /// This method reserves enough persistent buffer capacity for all reset
288    /// output before calling `encoder`. It does not flush pending units.
289    ///
290    /// # Type Parameters
291    ///
292    /// * `E` - Transcoder producing the wrapped output unit type.
293    /// * `M` - Mapper from transcoder errors into I/O errors.
294    /// * `Value` - Source value type accepted by the transcoder.
295    ///
296    /// # Parameters
297    ///
298    /// * `encoder` - Transcoder whose stream prefix is being collected.
299    /// * `map_error` - Mapper for reset failures.
300    ///
301    /// # Errors
302    ///
303    /// Returns capacity errors, allocation errors, or reset errors mapped by
304    /// `map_error`.
305    ///
306    /// # Panics
307    ///
308    /// Panics when `encoder` writes more than its declared reset bound.
309    pub fn reset<E, M, Value>(&mut self, encoder: &mut E, map_error: &mut M) -> Result<()>
310    where
311        E: Transcoder<Input = Value, Output = O::Item>,
312        M: FnMut(E::Error) -> Error,
313    {
314        let required = encoder.max_reset_output_len().map_err(capacity_error_to_invalid_data)?;
315        self.ensure_spare_capacity(required)?;
316        let (units, output_index, available) = self.output.spare_raw_parts_mut();
317        debug_assert!(
318            available >= required,
319            "insufficient reset capacity reserved in spare output buffer",
320        );
321        let written = encoder.reset(units, output_index).map_err(&mut *map_error)?;
322        assert!(written <= required, "reset wrote beyond its bound");
323        // SAFETY: The encoder reported initialized units within the spare
324        // range reserved above.
325        unsafe {
326            self.output.advance(written);
327        }
328        Ok(())
329    }
330
331    /// Encodes values from an indexed input range using a streaming
332    /// [`Transcoder`].
333    ///
334    /// This streaming path writes directly into the internal spare buffer and
335    /// grows it for any larger `NeedOutput.required` value reported by
336    /// `encoder`.
337    ///
338    /// # Type Parameters
339    ///
340    /// * `E` - Encoder producing the wrapped output unit type.
341    /// * `M` - Mapper from encoder errors into I/O errors.
342    /// * `Value` - Source value type accepted by the encoder.
343    ///
344    /// # Parameters
345    ///
346    /// * `encoder` - Streaming encoder used for this operation.
347    /// * `map_error` - Function mapping transcode errors into I/O errors.
348    /// * `input` - Source values.
349    /// * `input_index` - Start index inside `input`.
350    /// * `count` - Maximum number of values to encode.
351    ///
352    /// # Returns
353    ///
354    /// The number of source values consumed.
355    ///
356    /// # Errors
357    ///
358    /// Returns invalid input ranges, capacity, transcode, or output errors.
359    ///
360    /// # Examples
361    ///
362    /// A value that only implements [`Transcoder`] is not an encoder and cannot
363    /// be passed to this method.
364    ///
365    /// ```compile_fail
366    /// use std::io::Error;
367    /// use std::io::sink;
368    ///
369    /// use qubit_codec::CapacityError;
370    /// use qubit_codec::TranscodeEncodeError;
371    /// use qubit_codec::TranscodeEncodeOutput;
372    /// use qubit_codec::TranscodeProgress;
373    /// use qubit_codec::Transcoder;
374    ///
375    /// struct GenericTranscoder;
376    ///
377    /// impl Transcoder for GenericTranscoder {
378    ///     type Input = u8;
379    ///     type Output = u8;
380    ///     type Error = TranscodeEncodeError<(), u8>;
381    ///
382    ///     fn max_transcode_output_len(
383    ///         &self,
384    ///         input_len: usize,
385    ///     ) -> Result<usize, CapacityError> {
386    ///         Ok(input_len)
387    ///     }
388    ///
389    ///     fn reset(
390    ///         &mut self,
391    ///         _output: &mut [u8],
392    ///         _output_index: usize,
393    ///     ) -> Result<usize, Self::Error> {
394    ///         Ok(0)
395    ///     }
396    ///
397    ///     fn transcode(
398    ///         &mut self,
399    ///         input: &[u8],
400    ///         input_index: usize,
401    ///         _output: &mut [u8],
402    ///         _output_index: usize,
403    ///     ) -> Result<TranscodeProgress, Self::Error> {
404    ///         Ok(TranscodeProgress::complete(input.len() - input_index, 0))
405    ///     }
406    ///
407    ///     fn finish(
408    ///         &mut self,
409    ///         _output: &mut [u8],
410    ///         _output_index: usize,
411    ///     ) -> Result<usize, Self::Error> {
412    ///         Ok(0)
413    ///     }
414    /// }
415    ///
416    /// let mut output = TranscodeEncodeOutput::with_capacity(sink(), 1);
417    /// let mut transcoder = GenericTranscoder;
418    /// let mut map_error = |_| Error::other("transcode error");
419    /// let _ = output.transcode(
420    ///     &mut transcoder,
421    ///     &mut map_error,
422    ///     &[0_u8],
423    ///     0,
424    ///     1,
425    /// );
426    /// ```
427    pub fn transcode<E, M, Value>(
428        &mut self,
429        encoder: &mut E,
430        map_error: &mut M,
431        input: &[Value],
432        input_index: usize,
433        count: usize,
434    ) -> Result<usize>
435    where
436        E: TranscodeEncoder<Input = Value, Output = O::Item>,
437        M: FnMut(E::Error) -> Error,
438    {
439        let input_end = SliceRange::checked_range_end(
440            input.len(),
441            input_index,
442            count,
443            "encode input range exceeds source buffer",
444        )?;
445        if count == 0 {
446            return Ok(0);
447        }
448        let input = &input[..input_end];
449        let mut read_total = 0;
450        let mut required_spare = NonZeroUsize::MIN;
451        while read_total < count {
452            self.ensure_transcode_spare_capacity(required_spare)?;
453            let (units, output_index, available_output) = self.output.spare_raw_parts_mut();
454            debug_assert!(
455                available_output >= required_spare.get(),
456                "reserved spare buffer is smaller than required encoder output",
457            );
458            let remaining_input = count - read_total;
459            let progress = encoder
460                .transcode(input, input_index + read_total, units, output_index)
461                .map_err(&mut *map_error)?;
462            let progress = validate_encode_progress(
463                progress,
464                input_index + read_total,
465                remaining_input,
466                output_index,
467                available_output,
468            )?;
469            let read = progress.read();
470            let written = progress.written();
471            // SAFETY: The progress bounds check above proved that the encoder
472            // initialized no more than the available spare output window.
473            unsafe {
474                self.output.advance(written);
475            }
476            read_total += read;
477            if progress.is_complete() {
478                return Ok(read_total);
479            }
480            if let TranscodeStatus::NeedOutput { required, .. } = progress.status() {
481                required_spare = required;
482                if read_total == count {
483                    self.ensure_transcode_spare_capacity(required)?;
484                }
485            }
486        }
487        Ok(read_total)
488    }
489
490    /// Finishes the encoder and flushes the wrapped unit output.
491    ///
492    /// This method writes final units directly into the internal spare buffer,
493    /// growing it when [`Transcoder::max_finish_output_len`] exceeds the
494    /// current capacity.
495    ///
496    /// # Type Parameters
497    ///
498    /// * `E` - Transcoder producing the wrapped output unit type.
499    /// * `M` - Mapper from transcoder errors into I/O errors.
500    /// * `Value` - Source value type accepted by the transcoder.
501    ///
502    /// # Parameters
503    ///
504    /// * `encoder` - Encoder whose final units are being collected.
505    /// * `map_error` - Function mapping transcode errors into I/O errors.
506    ///
507    /// # Errors
508    ///
509    /// Returns capacity, transcode finalization, or wrapped output flush
510    /// errors.
511    ///
512    /// # Panics
513    ///
514    /// Panics when the encoder exceeds its declared finish-output bound.
515    pub fn finish<E, M, Value>(&mut self, encoder: &mut E, map_error: &mut M) -> Result<()>
516    where
517        E: Transcoder<Input = Value, Output = O::Item>,
518        M: FnMut(E::Error) -> Error,
519    {
520        self.finish_to_buffer(encoder, map_error)?;
521        self.output.flush()
522    }
523
524    /// Finishes the encoder while retaining final units in the output buffer.
525    ///
526    /// This method separates encoder finalization from output delivery. It is
527    /// useful when a caller must record successful finalization before a later
528    /// flush can fail. Call [`Self::flush`] to deliver the retained units.
529    ///
530    /// # Type Parameters
531    ///
532    /// * `E` - Transcoder producing the wrapped output unit type.
533    /// * `M` - Mapper from transcoder errors into I/O errors.
534    /// * `Value` - Source value type accepted by the transcoder.
535    ///
536    /// # Parameters
537    ///
538    /// * `encoder` - Encoder whose final units are being collected.
539    /// * `map_error` - Function mapping transcode errors into I/O errors.
540    ///
541    /// # Errors
542    ///
543    /// Returns capacity planning, allocation, or transcoder finalization
544    /// errors. It does not perform output I/O.
545    ///
546    /// # Panics
547    ///
548    /// Panics when `encoder` writes more units than its declared finish bound.
549    pub fn finish_to_buffer<E, M, Value>(&mut self, encoder: &mut E, map_error: &mut M) -> Result<()>
550    where
551        E: Transcoder<Input = Value, Output = O::Item>,
552        M: FnMut(E::Error) -> Error,
553    {
554        let required = match encoder.max_finish_output_len() {
555            Ok(required) => required,
556            Err(error) => return Err(capacity_error_to_invalid_data(error)),
557        };
558        self.ensure_spare_capacity(required)?;
559        let (units, output_index, available) = self.output.spare_raw_parts_mut();
560        debug_assert!(
561            available >= required,
562            "insufficient finish capacity reserved in spare output buffer",
563        );
564        let written = encoder.finish(units, output_index).map_err(&mut *map_error)?;
565        assert!(written <= required, "finish wrote beyond its bound");
566        // SAFETY: The encoder reported initialized units within the spare
567        // range that was reserved above.
568        unsafe {
569            self.output.advance(written);
570        }
571        Ok(())
572    }
573
574    /// Ensures enough output capacity for one non-zero required unit count.
575    ///
576    /// The non-zero wrapper preserves the engine's progress contract while
577    /// this adapter performs any necessary allocation.
578    ///
579    /// # Parameters
580    ///
581    /// * `required` - Minimum writable unit count requested by the engine.
582    ///
583    /// # Errors
584    ///
585    /// Propagates buffer allocation or wrapped-output flush errors.
586    fn ensure_transcode_spare_capacity(&mut self, required: NonZeroUsize) -> Result<()> {
587        self.ensure_spare_capacity(required.get())
588    }
589}
590
591impl<O> TranscodeEncodeOutput<O>
592where
593    O: Output<Item = u8> + Seekable<Unit = u8>,
594{
595    /// Flushes pending bytes, then seeks the wrapped byte output.
596    ///
597    /// # Parameters
598    ///
599    /// * `position` - Target seek position.
600    ///
601    /// # Returns
602    ///
603    /// The new stream position reported by the wrapped output.
604    ///
605    /// # Errors
606    ///
607    /// Returns flush or seek errors from the wrapped output.
608    pub fn seek(&mut self, position: SeekFrom) -> Result<u64> {
609        self.output.seek_to(position)
610    }
611}
612
613impl<O> Write for TranscodeEncodeOutput<O>
614where
615    O: Output<Item = u8>,
616{
617    /// Writes raw bytes through the internal buffer without encoding them.
618    ///
619    /// # Parameters
620    ///
621    /// * `input` - Bytes to append to the buffered unit output.
622    ///
623    /// # Returns
624    ///
625    /// The number of bytes accepted, which may be shorter than `input`.
626    ///
627    /// # Errors
628    ///
629    /// Propagates wrapped-output errors when buffer delivery is needed.
630    fn write(&mut self, input: &[u8]) -> Result<usize> {
631        Output::write(&mut self.output, input)
632    }
633
634    /// Writes all raw bytes through the internal buffer without encoding them.
635    ///
636    /// # Parameters
637    ///
638    /// * `input` - Bytes that must all be accepted before success.
639    ///
640    /// # Errors
641    ///
642    /// Propagates output errors and returns `WriteZero` if output stops
643    /// progressing.
644    ///
645    /// # Panics
646    ///
647    /// Panics when the wrapped output reports more bytes than supplied.
648    fn write_all(&mut self, input: &[u8]) -> Result<()> {
649        let mut written = 0;
650        while written < input.len() {
651            let count = Output::write(&mut self.output, &input[written..])?;
652            if count == 0 {
653                return Err(Error::from(ErrorKind::WriteZero));
654            }
655            assert!(
656                count <= input.len() - written,
657                "Output::write returned a count beyond the input length",
658            );
659            written += count;
660        }
661        Ok(())
662    }
663
664    /// Flushes buffered bytes to the wrapped output without finishing an
665    /// encoder.
666    ///
667    /// # Errors
668    ///
669    /// Propagates wrapped-output flush errors.
670    fn flush(&mut self) -> Result<()> {
671        TranscodeEncodeOutput::flush(self)
672    }
673}
674
675impl<O> Seek for TranscodeEncodeOutput<O>
676where
677    O: Output<Item = u8> + Seekable<Unit = u8>,
678{
679    /// Flushes pending bytes, then seeks the wrapped byte output.
680    ///
681    /// # Parameters
682    ///
683    /// * `position` - Target position relative to the selected seek origin.
684    ///
685    /// # Returns
686    ///
687    /// The new absolute byte position.
688    ///
689    /// # Errors
690    ///
691    /// Propagates flush or seek errors from the wrapped output.
692    fn seek(&mut self, position: SeekFrom) -> Result<u64> {
693        self.seek(position)
694    }
695}
696
697impl<O> fmt::Debug for TranscodeEncodeOutput<O>
698where
699    O: Output,
700    O::Item: Copy + Default,
701    BufferedOutput<O>: fmt::Debug,
702{
703    /// Formats the owned buffer and underlying output for diagnostics.
704    ///
705    /// # Parameters
706    ///
707    /// * `formatter` - Destination and formatting options.
708    ///
709    /// # Errors
710    ///
711    /// Returns a formatting error if the destination rejects output.
712    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
713        formatter
714            .debug_struct("TranscodeEncodeOutput")
715            .field("output", &self.output)
716            .finish()
717    }
718}
719
720/// Wraps a streaming capacity failure as `InvalidData`, retaining its source.
721///
722/// # Parameters
723///
724/// * `error` - Capacity planning failure from a streaming transcoder.
725///
726/// # Returns
727///
728/// An owned I/O error carrying the original capacity error.
729fn capacity_error_to_invalid_data(error: CapacityError) -> Error {
730    Error::new(ErrorKind::InvalidData, error)
731}
732
733/// Preserves successful values and maps one-value failures into I/O errors.
734///
735/// # Type Parameters
736///
737/// * `T` - Successful result value.
738/// * `E` - Codec-domain error passed to the mapper.
739/// * `Value` - Source value type carried by an unencodable error.
740///
741/// # Parameters
742///
743/// * `result` - Codec lifecycle result to translate.
744/// * `map_error` - Called only for a codec-domain failure.
745///
746/// # Returns
747///
748/// The original successful value without transformation.
749///
750/// # Errors
751///
752/// Returns mapped domain errors or `InvalidInput` for bounds and domain
753/// rejection.
754fn map_encode_value_result<T, E, Value>(
755    result: CoreResult<T, TranscodeEncodeError<E, Value>>,
756    map_error: &mut dyn FnMut(E) -> Error,
757) -> Result<T> {
758    match result {
759        Ok(value) => Ok(value),
760        Err(error) => Err(map_encode_value_error(error, map_error)),
761    }
762}
763
764/// Maps domain failures through the caller and other failures to
765/// `InvalidInput`.
766///
767/// # Type Parameters
768///
769/// * `E` - Codec-domain error passed to the mapper.
770/// * `Value` - Rejected source value type, discarded during I/O translation.
771///
772/// # Parameters
773///
774/// * `error` - Encoding failure consumed by this translation.
775/// * `map_error` - Invoked only for the `Domain` variant.
776///
777/// # Returns
778///
779/// An I/O error retaining mapped domain detail or a bound/rejection message.
780#[inline(never)]
781fn map_encode_value_error<E, Value>(
782    error: TranscodeEncodeError<E, Value>,
783    map_error: &mut dyn FnMut(E) -> Error,
784) -> Error {
785    match error {
786        TranscodeEncodeError::Domain(error) => map_error(error.into_source()),
787        TranscodeEncodeError::Unencodable { .. } => Error::new(ErrorKind::InvalidInput, "codec cannot encode value"),
788        TranscodeEncodeError::Failure(_) => Error::new(ErrorKind::InvalidInput, "codec output bound overflow"),
789    }
790}