1mod syn_ext;
2pub mod r#trait;
3pub mod types;
4
5use std::borrow::Cow;
6use std::{fs, io};
7
8use proc_macro2::TokenStream;
9use quote::quote;
10use sha2::{Digest, Sha256};
11use stellar_xdr::{ScSpecEntry, ScSpecTypeDef, ScSpecTypeUdt, ScSpecUdtUnionCaseV0};
12use syn::Error;
13
14use soroban_spec::read::{from_wasm, FromWasmError};
15
16use types::{
17 generate_enum_with_options, generate_error_enum_with_options, generate_event_with_options,
18 generate_struct_with_options, generate_union_with_options,
19};
20pub use types::{GenerateError, GenerateOptions};
21
22#[derive(thiserror::Error, Debug)]
28pub enum GenerateFromFileError {
29 #[error("reading file: {0}")]
30 Io(io::Error),
31 #[error("sha256 does not match, expected: {expected}")]
32 VerifySha256 { expected: String },
33 #[error("parsing contract spec: {0}")]
34 Parse(stellar_xdr::Error),
35 #[error("getting contract spec: {0}")]
36 GetSpec(FromWasmError),
37 #[error("generating code: {0}")]
38 Generate(GenerateError),
39}
40
41pub fn generate_from_file(
42 file: &str,
43 verify_sha256: Option<&str>,
44) -> Result<TokenStream, GenerateFromFileError> {
45 let wasm = fs::read(file).map_err(GenerateFromFileError::Io)?;
47
48 let code = generate_from_wasm(&wasm, file, verify_sha256)?;
50 Ok(code)
51}
52
53pub fn generate_from_wasm(
54 wasm: &[u8],
55 file: &str,
56 verify_sha256: Option<&str>,
57) -> Result<TokenStream, GenerateFromFileError> {
58 generate_from_wasm_with_options(wasm, file, verify_sha256, &GenerateOptions::default())
59}
60
61pub fn generate_from_wasm_with_options(
62 wasm: &[u8],
63 file: &str,
64 verify_sha256: Option<&str>,
65 opts: &GenerateOptions,
66) -> Result<TokenStream, GenerateFromFileError> {
67 let sha256 = Sha256::digest(wasm);
68 let sha256 = format!("{:x}", sha256);
69 if let Some(verify_sha256) = verify_sha256 {
70 if verify_sha256 != sha256 {
71 return Err(GenerateFromFileError::VerifySha256 { expected: sha256 });
72 }
73 }
74
75 let spec = from_wasm(wasm).map_err(GenerateFromFileError::GetSpec)?;
76 let code = generate_with_options(&spec, file, &sha256, opts)
77 .map_err(GenerateFromFileError::Generate)?;
78 Ok(code)
79}
80
81pub fn generate(
82 specs: &[ScSpecEntry],
83 file: &str,
84 sha256: &str,
85) -> Result<TokenStream, GenerateError> {
86 generate_with_options(specs, file, sha256, &GenerateOptions::default())
87}
88
89pub fn generate_with_options(
90 specs: &[ScSpecEntry],
91 file: &str,
92 sha256: &str,
93 opts: &GenerateOptions,
94) -> Result<TokenStream, GenerateError> {
95 let generated = generate_without_file_with_options(specs, opts)?;
96 Ok(quote! {
97 pub const WASM: &[u8] = soroban_sdk::contractfile!(file = #file, sha256 = #sha256);
98 #generated
99 })
100}
101
102pub fn generate_without_file(specs: &[ScSpecEntry]) -> Result<TokenStream, GenerateError> {
103 generate_without_file_with_options(specs, &GenerateOptions::default())
104}
105
106pub fn generate_without_file_with_options(
107 specs: &[ScSpecEntry],
108 opts: &GenerateOptions,
109) -> Result<TokenStream, GenerateError> {
110 let specs = apply_error_udt_override(specs);
111 let specs: &[ScSpecEntry] = &specs;
112
113 let mut spec_fns = Vec::new();
114 let mut spec_structs = Vec::new();
115 let mut spec_unions = Vec::new();
116 let mut spec_enums = Vec::new();
117 let mut spec_error_enums = Vec::new();
118 let mut spec_events = Vec::new();
119 for s in specs {
120 match s {
121 ScSpecEntry::FunctionV0(f) => spec_fns.push(f),
122 ScSpecEntry::UdtStructV0(s) => spec_structs.push(s),
123 ScSpecEntry::UdtUnionV0(u) => spec_unions.push(u),
124 ScSpecEntry::UdtEnumV0(e) => spec_enums.push(e),
125 ScSpecEntry::UdtErrorEnumV0(e) => spec_error_enums.push(e),
126 ScSpecEntry::EventV0(e) => spec_events.push(e),
127 }
128 }
129
130 let trait_name = "Contract";
131
132 let trait_ = r#trait::generate_trait(trait_name, &spec_fns)?;
133 let structs = spec_structs
134 .iter()
135 .map(|s| generate_struct_with_options(s, opts))
136 .collect::<Result<Vec<_>, _>>()?;
137 let unions = spec_unions
138 .iter()
139 .map(|s| generate_union_with_options(s, opts))
140 .collect::<Result<Vec<_>, _>>()?;
141 let enums = spec_enums
142 .iter()
143 .map(|s| generate_enum_with_options(s, opts))
144 .collect::<Result<Vec<_>, _>>()?;
145 let error_enums = spec_error_enums
146 .iter()
147 .map(|s| generate_error_enum_with_options(s, opts))
148 .collect::<Result<Vec<_>, _>>()?;
149 let events = spec_events
150 .iter()
151 .map(|s| generate_event_with_options(s, opts))
152 .collect::<Result<Vec<_>, _>>()?;
153
154 Ok(quote! {
155 #[soroban_sdk::contractargs(name = "Args")]
156 #[soroban_sdk::contractclient(name = "Client")]
157 #trait_
158
159 #(#structs)*
160 #(#unions)*
161 #(#enums)*
162 #(#error_enums)*
163 #(#events)*
164 })
165}
166
167fn apply_error_udt_override(specs: &[ScSpecEntry]) -> Cow<'_, [ScSpecEntry]> {
183 let has_error_udt = specs.iter().any(|e| {
184 matches!(
185 e,
186 ScSpecEntry::UdtErrorEnumV0(err) if err.name.to_utf8_string_lossy() == "Error"
187 )
188 });
189 if has_error_udt {
190 let mut v = specs.to_vec();
191 rewrite_error_to_udt(&mut v);
192 Cow::Owned(v)
193 } else {
194 Cow::Borrowed(specs)
195 }
196}
197
198fn rewrite_error_to_udt(entries: &mut [ScSpecEntry]) {
203 fn rewrite_ty(t: &mut ScSpecTypeDef) {
204 match t {
205 ScSpecTypeDef::Error => {
206 *t = ScSpecTypeDef::Udt(ScSpecTypeUdt {
207 name: "Error".try_into().unwrap(),
208 });
209 }
210 ScSpecTypeDef::Option(o) => rewrite_ty(&mut o.value_type),
211 ScSpecTypeDef::Result(r) => {
212 rewrite_ty(&mut r.ok_type);
213 rewrite_ty(&mut r.error_type);
214 }
215 ScSpecTypeDef::Vec(v) => rewrite_ty(&mut v.element_type),
216 ScSpecTypeDef::Map(m) => {
217 rewrite_ty(&mut m.key_type);
218 rewrite_ty(&mut m.value_type);
219 }
220 ScSpecTypeDef::Tuple(tu) => {
221 for vt in tu.value_types.iter_mut() {
222 rewrite_ty(vt);
223 }
224 }
225 _ => {}
226 }
227 }
228 for entry in entries.iter_mut() {
229 match entry {
230 ScSpecEntry::FunctionV0(f) => {
231 for input in f.inputs.iter_mut() {
232 rewrite_ty(&mut input.type_);
233 }
234 for output in f.outputs.iter_mut() {
235 rewrite_ty(output);
236 }
237 }
238 ScSpecEntry::UdtStructV0(s) => {
239 for field in s.fields.iter_mut() {
240 rewrite_ty(&mut field.type_);
241 }
242 }
243 ScSpecEntry::UdtUnionV0(u) => {
244 for case in u.cases.iter_mut() {
245 if let ScSpecUdtUnionCaseV0::TupleV0(t) = case {
246 for ty in t.type_.iter_mut() {
247 rewrite_ty(ty);
248 }
249 }
250 }
251 }
252 ScSpecEntry::UdtEnumV0(_) | ScSpecEntry::UdtErrorEnumV0(_) => {}
253 ScSpecEntry::EventV0(e) => {
254 for p in e.params.iter_mut() {
255 rewrite_ty(&mut p.type_);
256 }
257 }
258 }
259 }
260}
261
262pub trait ToFormattedString {
265 fn to_formatted_string(&self) -> Result<String, Error>;
269}
270
271impl ToFormattedString for TokenStream {
272 fn to_formatted_string(&self) -> Result<String, Error> {
273 let file = syn::parse2(self.clone())?;
274 Ok(prettyplease::unparse(&file))
275 }
276}
277
278#[cfg(test)]
279mod test {
280 use pretty_assertions::assert_eq;
281
282 use super::{generate, ToFormattedString};
283 use soroban_spec::read::from_wasm;
284
285 const EXAMPLE_WASM: &[u8] = include_bytes!("../../target/wasm32v1-none/release/test_udt.wasm");
286
287 #[test]
288 fn example() {
289 let entries = from_wasm(EXAMPLE_WASM).unwrap();
290 let rust = generate(&entries, "<file>", "<sha256>")
291 .unwrap()
292 .to_formatted_string()
293 .unwrap();
294 assert_eq!(
295 rust,
296 r#"pub const WASM: &[u8] = soroban_sdk::contractfile!(file = "<file>", sha256 = "<sha256>");
297#[soroban_sdk::contractargs(name = "Args")]
298#[soroban_sdk::contractclient(name = "Client")]
299pub trait Contract {
300 fn add(env: soroban_sdk::Env, a: UdtEnum, b: UdtEnum) -> i64;
301 fn recursive(env: soroban_sdk::Env, a: UdtRecursive) -> Option<UdtRecursive>;
302 fn recursive_enum(
303 env: soroban_sdk::Env,
304 a: RecursiveEnum,
305 key: u32,
306 ) -> Result<Option<RecursiveEnum>, soroban_sdk::Error>;
307}
308#[soroban_sdk::contracttype(export = false)]
309#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
310pub struct UdtTuple(pub i64, pub soroban_sdk::Vec<i64>);
311#[soroban_sdk::contracttype(export = false)]
312#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
313pub struct UdtStruct {
314 pub a: i64,
315 pub b: i64,
316 pub c: soroban_sdk::Vec<i64>,
317}
318#[soroban_sdk::contracttype(export = false)]
319#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
320pub struct UdtRecursive {
321 pub a: soroban_sdk::Symbol,
322 pub b: soroban_sdk::Vec<UdtRecursive>,
323}
324#[soroban_sdk::contracttype(export = false)]
325#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
326pub struct RecursiveToEnum {
327 pub a: soroban_sdk::Symbol,
328 pub b: soroban_sdk::Map<u32, RecursiveEnum>,
329}
330#[soroban_sdk::contracttype(export = false)]
331#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
332pub struct ContractContext {
333 pub args: soroban_sdk::Vec<soroban_sdk::Val>,
334 pub contract: soroban_sdk::Address,
335 pub fn_name: soroban_sdk::Symbol,
336}
337#[soroban_sdk::contracttype(export = false)]
338#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
339pub struct SubContractInvocation {
340 pub context: ContractContext,
341 pub sub_invocations: soroban_sdk::Vec<InvokerContractAuthEntry>,
342}
343#[soroban_sdk::contracttype(export = false)]
344#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
345pub struct CreateContractHostFnContext {
346 pub executable: ContractExecutable,
347 pub salt: soroban_sdk::BytesN<32>,
348}
349#[soroban_sdk::contracttype(export = false)]
350#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
351pub struct CreateContractWithConstructorHostFnContext {
352 pub constructor_args: soroban_sdk::Vec<soroban_sdk::Val>,
353 pub executable: ContractExecutable,
354 pub salt: soroban_sdk::BytesN<32>,
355}
356#[soroban_sdk::contracttype(export = false)]
357#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
358pub enum UdtEnum {
359 UdtA,
360 UdtB(UdtStruct),
361 UdtC(UdtEnum2),
362 UdtD(UdtTuple),
363}
364#[soroban_sdk::contracttype(export = false)]
365#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
366pub enum RecursiveEnum {
367 NotRecursive,
368 Recursive(RecursiveToEnum),
369}
370#[soroban_sdk::contracttype(export = false)]
371#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
372pub enum Context {
373 Contract(ContractContext),
374 CreateContractHostFn(CreateContractHostFnContext),
375 CreateContractWithCtorHostFn(CreateContractWithConstructorHostFnContext),
376}
377#[soroban_sdk::contracttype(export = false)]
378#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
379pub enum ContractExecutable {
380 Wasm(soroban_sdk::BytesN<32>),
381}
382#[soroban_sdk::contracttype(export = false)]
383#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
384pub enum InvokerContractAuthEntry {
385 Contract(SubContractInvocation),
386 CreateContractHostFn(CreateContractHostFnContext),
387 CreateContractWithCtorHostFn(CreateContractWithConstructorHostFnContext),
388}
389#[soroban_sdk::contracttype(export = false)]
390#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
391pub enum Executable {
392 Wasm(soroban_sdk::BytesN<32>),
393 StellarAsset,
394 Account,
395}
396#[soroban_sdk::contracttype(export = false)]
397#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
398pub enum UdtEnum2 {
399 A = 10,
400 B = 15,
401}
402"#,
403 );
404 }
405
406 const ADD_U64_WASM: &[u8] =
407 include_bytes!("../../target/wasm32v1-none/release/test_add_u64.wasm");
408
409 #[test]
414 fn test_add_u64_result_types() {
415 let entries = from_wasm(ADD_U64_WASM).unwrap();
416 let rust = generate(&entries, "<file>", "<sha256>")
417 .unwrap()
418 .to_formatted_string()
419 .unwrap();
420 assert_eq!(
421 rust,
422 r#"pub const WASM: &[u8] = soroban_sdk::contractfile!(file = "<file>", sha256 = "<sha256>");
423#[soroban_sdk::contractargs(name = "Args")]
424#[soroban_sdk::contractclient(name = "Client")]
425pub trait Contract {
426 fn add(env: soroban_sdk::Env, a: u64, b: u64) -> u64;
427 fn safe_add(env: soroban_sdk::Env, a: u64, b: u64) -> Result<u64, Error>;
428 fn safe_add_two(env: soroban_sdk::Env, a: u64, b: u64) -> Result<u64, MyError>;
429}
430#[soroban_sdk::contracttype(export = false)]
431#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
432pub struct ContractContext {
433 pub args: soroban_sdk::Vec<soroban_sdk::Val>,
434 pub contract: soroban_sdk::Address,
435 pub fn_name: soroban_sdk::Symbol,
436}
437#[soroban_sdk::contracttype(export = false)]
438#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
439pub struct SubContractInvocation {
440 pub context: ContractContext,
441 pub sub_invocations: soroban_sdk::Vec<InvokerContractAuthEntry>,
442}
443#[soroban_sdk::contracttype(export = false)]
444#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
445pub struct CreateContractHostFnContext {
446 pub executable: ContractExecutable,
447 pub salt: soroban_sdk::BytesN<32>,
448}
449#[soroban_sdk::contracttype(export = false)]
450#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
451pub struct CreateContractWithConstructorHostFnContext {
452 pub constructor_args: soroban_sdk::Vec<soroban_sdk::Val>,
453 pub executable: ContractExecutable,
454 pub salt: soroban_sdk::BytesN<32>,
455}
456#[soroban_sdk::contracttype(export = false)]
457#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
458pub enum Context {
459 Contract(ContractContext),
460 CreateContractHostFn(CreateContractHostFnContext),
461 CreateContractWithCtorHostFn(CreateContractWithConstructorHostFnContext),
462}
463#[soroban_sdk::contracttype(export = false)]
464#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
465pub enum ContractExecutable {
466 Wasm(soroban_sdk::BytesN<32>),
467}
468#[soroban_sdk::contracttype(export = false)]
469#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
470pub enum InvokerContractAuthEntry {
471 Contract(SubContractInvocation),
472 CreateContractHostFn(CreateContractHostFnContext),
473 CreateContractWithCtorHostFn(CreateContractWithConstructorHostFnContext),
474}
475#[soroban_sdk::contracttype(export = false)]
476#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
477pub enum Executable {
478 Wasm(soroban_sdk::BytesN<32>),
479 StellarAsset,
480 Account,
481}
482#[soroban_sdk::contracterror(export = false)]
483#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
484pub enum Error {
485 Overflow = 1,
486}
487#[soroban_sdk::contracterror(export = false)]
488#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
489pub enum MyError {
490 Overflow = 1,
491}
492"#,
493 );
494 }
495
496 #[test]
503 fn test_add_u64_spec_entries() {
504 use super::ScSpecEntry;
505 use stellar_xdr::ScSpecTypeDef;
506
507 let entries = from_wasm(ADD_U64_WASM).unwrap();
508
509 let safe_add_fn = entries
511 .iter()
512 .find_map(|e| match e {
513 ScSpecEntry::FunctionV0(f) if f.name.to_utf8_string().unwrap() == "safe_add" => {
514 Some(f)
515 }
516 _ => None,
517 })
518 .expect("safe_add function not found");
519
520 let output = safe_add_fn.outputs.to_option().expect("should have output");
521 let ScSpecTypeDef::Result(r) = output else {
522 panic!("output should be a Result type");
523 };
524 assert!(
525 matches!(r.ok_type.as_ref(), ScSpecTypeDef::U64),
526 "ok_type should be U64"
527 );
528 assert!(
529 matches!(r.error_type.as_ref(), ScSpecTypeDef::Error),
530 "error_type should be the built-in Error in the wasm spec, got {:?}",
531 r.error_type
532 );
533
534 let safe_add_two_fn = entries
536 .iter()
537 .find_map(|e| match e {
538 ScSpecEntry::FunctionV0(f)
539 if f.name.to_utf8_string().unwrap() == "safe_add_two" =>
540 {
541 Some(f)
542 }
543 _ => None,
544 })
545 .expect("safe_add_two function not found");
546
547 let output = safe_add_two_fn
548 .outputs
549 .to_option()
550 .expect("should have output");
551 let ScSpecTypeDef::Result(r) = output else {
552 panic!("output should be a Result type");
553 };
554 assert!(
555 matches!(r.ok_type.as_ref(), ScSpecTypeDef::U64),
556 "ok_type should be U64"
557 );
558 let ScSpecTypeDef::Udt(u) = r.error_type.as_ref() else {
559 panic!(
560 "error_type should be a UDT for MyError, got {:?}",
561 r.error_type
562 );
563 };
564 assert_eq!(
565 u.name.to_utf8_string().unwrap(),
566 "MyError",
567 "error_type should be MyError UDT"
568 );
569 }
570
571 #[test]
577 fn test_missing_error_udt_falls_back_to_sdk_error() {
578 use super::ScSpecEntry;
579 use stellar_xdr::{ScSpecFunctionV0, ScSpecTypeDef, ScSpecTypeResult};
580
581 let func = ScSpecFunctionV0 {
582 doc: "".try_into().unwrap(),
583 name: "safe_add".try_into().unwrap(),
584 inputs: [].try_into().unwrap(),
585 outputs: [ScSpecTypeDef::Result(Box::new(ScSpecTypeResult {
586 ok_type: Box::new(ScSpecTypeDef::U64),
587 error_type: Box::new(ScSpecTypeDef::Error),
588 }))]
589 .try_into()
590 .unwrap(),
591 };
592 let entries = [ScSpecEntry::FunctionV0(func)];
593 let rust = generate(&entries, "<file>", "<sha256>")
594 .unwrap()
595 .to_formatted_string()
596 .unwrap();
597 assert_eq!(
598 rust,
599 r#"pub const WASM: &[u8] = soroban_sdk::contractfile!(file = "<file>", sha256 = "<sha256>");
600#[soroban_sdk::contractargs(name = "Args")]
601#[soroban_sdk::contractclient(name = "Client")]
602pub trait Contract {
603 fn safe_add(env: soroban_sdk::Env) -> Result<u64, soroban_sdk::Error>;
604}
605"#,
606 );
607 }
608
609 #[test]
613 fn test_error_udt_overrides_sdk_error() {
614 use super::ScSpecEntry;
615 use stellar_xdr::{
616 ScSpecFunctionV0, ScSpecTypeDef, ScSpecTypeResult, ScSpecUdtErrorEnumCaseV0,
617 ScSpecUdtErrorEnumV0,
618 };
619
620 let func = ScSpecFunctionV0 {
621 doc: "".try_into().unwrap(),
622 name: "safe_add".try_into().unwrap(),
623 inputs: [].try_into().unwrap(),
624 outputs: [ScSpecTypeDef::Result(Box::new(ScSpecTypeResult {
625 ok_type: Box::new(ScSpecTypeDef::U64),
626 error_type: Box::new(ScSpecTypeDef::Error),
627 }))]
628 .try_into()
629 .unwrap(),
630 };
631 let error_enum = ScSpecUdtErrorEnumV0 {
632 doc: "".try_into().unwrap(),
633 lib: "".try_into().unwrap(),
634 name: "Error".try_into().unwrap(),
635 cases: [ScSpecUdtErrorEnumCaseV0 {
636 doc: "".try_into().unwrap(),
637 name: "Overflow".try_into().unwrap(),
638 value: 1,
639 }]
640 .try_into()
641 .unwrap(),
642 };
643 let entries = [
644 ScSpecEntry::FunctionV0(func),
645 ScSpecEntry::UdtErrorEnumV0(error_enum),
646 ];
647 let rust = generate(&entries, "<file>", "<sha256>")
648 .unwrap()
649 .to_formatted_string()
650 .unwrap();
651 assert_eq!(
652 rust,
653 r#"pub const WASM: &[u8] = soroban_sdk::contractfile!(file = "<file>", sha256 = "<sha256>");
654#[soroban_sdk::contractargs(name = "Args")]
655#[soroban_sdk::contractclient(name = "Client")]
656pub trait Contract {
657 fn safe_add(env: soroban_sdk::Env) -> Result<u64, Error>;
658}
659#[soroban_sdk::contracterror(export = false)]
660#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
661pub enum Error {
662 Overflow = 1,
663}
664"#,
665 );
666 }
667
668 #[test]
671 fn test_error_udt_override_rewrites_nested_vec() {
672 use super::ScSpecEntry;
673 use stellar_xdr::{
674 ScSpecFunctionV0, ScSpecTypeDef, ScSpecTypeVec, ScSpecUdtErrorEnumCaseV0,
675 ScSpecUdtErrorEnumV0,
676 };
677
678 let func = ScSpecFunctionV0 {
679 doc: "".try_into().unwrap(),
680 name: "errors".try_into().unwrap(),
681 inputs: [].try_into().unwrap(),
682 outputs: [ScSpecTypeDef::Vec(Box::new(ScSpecTypeVec {
683 element_type: Box::new(ScSpecTypeDef::Error),
684 }))]
685 .try_into()
686 .unwrap(),
687 };
688 let error_enum = ScSpecUdtErrorEnumV0 {
689 doc: "".try_into().unwrap(),
690 lib: "".try_into().unwrap(),
691 name: "Error".try_into().unwrap(),
692 cases: [ScSpecUdtErrorEnumCaseV0 {
693 doc: "".try_into().unwrap(),
694 name: "Overflow".try_into().unwrap(),
695 value: 1,
696 }]
697 .try_into()
698 .unwrap(),
699 };
700 let entries = [
701 ScSpecEntry::FunctionV0(func),
702 ScSpecEntry::UdtErrorEnumV0(error_enum),
703 ];
704 let rust = generate(&entries, "<file>", "<sha256>")
705 .unwrap()
706 .to_formatted_string()
707 .unwrap();
708 assert_eq!(
709 rust,
710 r#"pub const WASM: &[u8] = soroban_sdk::contractfile!(file = "<file>", sha256 = "<sha256>");
711#[soroban_sdk::contractargs(name = "Args")]
712#[soroban_sdk::contractclient(name = "Client")]
713pub trait Contract {
714 fn errors(env: soroban_sdk::Env) -> soroban_sdk::Vec<Error>;
715}
716#[soroban_sdk::contracterror(export = false)]
717#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
718pub enum Error {
719 Overflow = 1,
720}
721"#,
722 );
723 }
724}