@@ -20,12 +20,20 @@ use pyo3_stub_gen::{define_stub_info_gatherer, derive::gen_stub_pyfunction};
2020///
2121/// >>> cbor2.loads(diag2cbor("[1, spam'eggs']", to999=True))
2222/// [1, CBORTag(999, ['spam', 'eggs'])]
23+ ///
24+ /// * With `seq=True`, [CBOR sequences](https://datatracker.ietf.org/doc/html/rfc8742)
25+ /// are tolerated:
26+ ///
27+ /// >>> diag2cbor("1, 2, 3", seq=True)
28+ /// '\x01\x02\x03'
2329#[ gen_stub_pyfunction]
24- #[ pyfunction( signature = ( diagnostic, * , to999=false ) ) ]
25- fn diag2cbor ( py : Python < ' _ > , diagnostic : & str , to999 : bool ) -> PyResult < Py < PyBytes > > {
26- let mut data = cbor_edn:: StandaloneItem :: parse ( diagnostic)
30+ #[ pyfunction( signature = ( diagnostic, * , to999=false , seq= false ) ) ]
31+ fn diag2cbor ( py : Python < ' _ > , diagnostic : & str , to999 : bool , seq : bool ) -> PyResult < Py < PyBytes > > {
32+ let mut data = cbor_edn:: Sequence :: parse ( diagnostic)
2733 . map_err ( |e| pyo3:: exceptions:: PyValueError :: new_err ( format ! ( "{}" , e) ) ) ?;
2834
35+ check_sequence_expectation ( & data, seq) ?;
36+
2937 data. visit_application_literals ( & mut cbor_edn:: application:: all_aol_to_item) ;
3038 if to999 {
3139 data. visit_application_literals ( & mut cbor_edn:: application:: any_aol_to_tag999) ;
@@ -60,23 +68,36 @@ fn diag2cbor(py: Python<'_>, diagnostic: &str, to999: bool) -> PyResult<Py<PyByt
6068/// >>> cbor2diag(cbor2.dumps([1, 2]), pretty=False)
6169/// '[1,2]'
6270///
71+ /// * With `seq=True`, [CBOR sequences](https://datatracker.ietf.org/doc/html/rfc8742)
72+ /// are tolerated:
73+ ///
74+ /// >>> print(cbor2diag('\x01\x02\x03', seq=True))
75+ /// 1,
76+ /// 2,
77+ /// 3
78+ /// <BLANKLINE>
79+ ///
6380/// * With ``from999=True``, CBOR tag 999 will be rendered as application oriented literal. Unlike
6481/// other tags, this does not happen by default, as that tag is not intended to be used that way
6582/// by default.
6683///
6784/// >>> cbor2diag(bytes.fromhex("d9 03e7 82 63 666f6f 63 626172"), from999=True)
6885/// "foo'bar'"
6986#[ gen_stub_pyfunction]
70- #[ pyfunction( signature = ( encoded, * , pretty=true , from999=false ) ) ]
87+ #[ pyfunction( signature = ( encoded, * , pretty=true , from999=false , seq= false ) ) ]
7188fn cbor2diag (
7289 _py : Python < ' _ > ,
7390 // Staying generic for compatibility (we do still accept a [int]), but declare just bytes.
7491 #[ gen_stub( override_type( type_repr = "bytes" ) ) ] encoded : & [ u8 ] ,
7592 pretty : bool ,
7693 from999 : bool ,
94+ seq : bool ,
7795) -> PyResult < String > {
78- let mut parsed = cbor_edn:: StandaloneItem :: from_cbor ( encoded)
96+ let mut parsed = cbor_edn:: Sequence :: from_cbor ( encoded)
7997 . map_err ( |e| pyo3:: exceptions:: PyValueError :: new_err ( format ! ( "{}" , e) ) ) ?;
98+
99+ check_sequence_expectation ( & parsed, seq) ?;
100+
80101 if pretty {
81102 parsed. visit_tag ( & mut cbor_edn:: application:: all_tag_prettify) ;
82103 }
@@ -111,6 +132,24 @@ fn cbor2diag(
111132 Ok ( parsed. serialize ( ) )
112133}
113134
135+ /// "Raises" a ValueError if the sequence is really a sequence (and not really just a single item),
136+ /// unless `seq=true` (i.e., the user *asked* for sequence handling).
137+ fn check_sequence_expectation ( data : & cbor_edn:: Sequence , seq : bool ) -> PyResult < ( ) > {
138+ if seq {
139+ return Ok ( ( ) ) ;
140+ }
141+
142+ let count = data. items ( ) . count ( ) ;
143+ if count == 1 {
144+ Ok ( ( ) )
145+ } else {
146+ Err ( pyo3:: exceptions:: PyValueError :: new_err ( format ! (
147+ "Expected single item, found sequence of {}" ,
148+ count
149+ ) ) )
150+ }
151+ }
152+
114153/// This provides conversion functions between CBOR's diagnostic notation (EDN) and its binary
115154/// representation.
116155///
0 commit comments