commit 39069b2097c0429e4c256736993a4fa38308b029
parent 3e11aa831c75819a7e26e5255c6141b860d50c72
Author: Antoine A <>
Date: Fri, 24 Apr 2026 10:37:59 +0200
ebics: pain001 gen
Diffstat:
14 files changed, 400 insertions(+), 64 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -1587,7 +1587,7 @@ dependencies = [
"jiff",
"pem",
"pretty_assertions",
- "rand 0.10.0",
+ "rand 0.10.1",
"rcgen",
"reedline",
"regex",
@@ -2053,7 +2053,7 @@ dependencies = [
"bytes",
"getrandom 0.3.4",
"lru-slab",
- "rand 0.9.2",
+ "rand 0.9.3",
"ring",
"rustc-hash",
"rustls",
@@ -2113,9 +2113,9 @@ dependencies = [
[[package]]
name = "rand"
-version = "0.9.2"
+version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
+checksum = "7ec095654a25171c2124e9e3393a930bddbffdc939556c914957a4c3e0a87166"
dependencies = [
"rand_chacha 0.9.0",
"rand_core 0.9.5",
@@ -2123,9 +2123,9 @@ dependencies = [
[[package]]
name = "rand"
-version = "0.10.0"
+version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8"
+checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207"
dependencies = [
"chacha20",
"getrandom 0.4.2",
@@ -3069,6 +3069,9 @@ dependencies = [
[[package]]
name = "taler-build"
version = "1.5.0"
+dependencies = [
+ "const_format",
+]
[[package]]
name = "taler-common"
@@ -3081,7 +3084,7 @@ dependencies = [
"glob",
"indexmap",
"jiff",
- "rand 0.10.0",
+ "rand 0.10.1",
"regex",
"serde",
"serde_json",
@@ -3525,7 +3528,7 @@ checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9"
dependencies = [
"getrandom 0.4.2",
"js-sys",
- "rand 0.10.0",
+ "rand 0.10.1",
"wasm-bindgen",
]
diff --git a/libeufin-nexus/sample/platform/gls_pain001.xml b/libeufin-nexus/sample/platform/gls_pain001.xml
@@ -1 +1 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09 pain.001.001.09.xsd"><CstmrCdtTrfInitn><GrpHdr><MsgId>MESSAGE_ID</MsgId><CreDtTm>2024-09-09T00:00:00Z</CreDtTm><NbOfTxs>3</NbOfTxs><CtrlSum>47.32</CtrlSum><InitgPty><Nm>myname</Nm></InitgPty></GrpHdr><PmtInf><PmtInfId>NOTPROVIDED</PmtInfId><PmtMtd>TRF</PmtMtd><BtchBookg>false</BtchBookg><NbOfTxs>3</NbOfTxs><CtrlSum>47.32</CtrlSum><PmtTpInf><SvcLvl><Cd>SEPA</Cd></SvcLvl></PmtTpInf><ReqdExctnDt><Dt>2024-09-09Z</Dt></ReqdExctnDt><Dbtr><Nm>myname</Nm></Dbtr><DbtrAcct><Id><IBAN>CH7789144474425692816</IBAN></Id></DbtrAcct><DbtrAgt><FinInstnId><BICFI>BIC</BICFI></FinInstnId></DbtrAgt><ChrgBr>SLEV</ChrgBr><CdtTrfTxInf><PmtId><InstrId>TX_FIRST</InstrId><EndToEndId>TX_FIRST</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">42</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 42</Ustrd></RmtInf></CdtTrfTxInf><CdtTrfTxInf><PmtId><InstrId>TX_SECOND</InstrId><EndToEndId>TX_SECOND</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">5.11</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 5.11</Ustrd></RmtInf></CdtTrfTxInf><CdtTrfTxInf><PmtId><InstrId>TX_THIRD</InstrId><EndToEndId>TX_THIRD</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">0.21</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 0.21</Ustrd></RmtInf></CdtTrfTxInf></PmtInf></CstmrCdtTrfInitn></Document>
-\ No newline at end of file
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09 pain.001.001.09.xsd"><CstmrCdtTrfInitn><GrpHdr><MsgId>MESSAGE_ID</MsgId><CreDtTm>2024-09-09T00:00:00Z</CreDtTm><NbOfTxs>3</NbOfTxs><CtrlSum>47.32</CtrlSum><InitgPty><Nm>myname</Nm></InitgPty></GrpHdr><PmtInf><PmtInfId>NOTPROVIDED</PmtInfId><PmtMtd>TRF</PmtMtd><BtchBookg>false</BtchBookg><NbOfTxs>3</NbOfTxs><CtrlSum>47.32</CtrlSum><PmtTpInf><SvcLvl><Cd>SEPA</Cd></SvcLvl></PmtTpInf><ReqdExctnDt><Dt>2024-09-09Z</Dt></ReqdExctnDt><Dbtr><Nm>myname</Nm></Dbtr><DbtrAcct><Id><IBAN>CH7789144474425692816</IBAN></Id></DbtrAcct><DbtrAgt><FinInstnId><BICFI>AAAABBCC123</BICFI></FinInstnId></DbtrAgt><ChrgBr>SLEV</ChrgBr><CdtTrfTxInf><PmtId><InstrId>TX_FIRST</InstrId><EndToEndId>TX_FIRST</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">42</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 42</Ustrd></RmtInf></CdtTrfTxInf><CdtTrfTxInf><PmtId><InstrId>TX_SECOND</InstrId><EndToEndId>TX_SECOND</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">5.11</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 5.11</Ustrd></RmtInf></CdtTrfTxInf><CdtTrfTxInf><PmtId><InstrId>TX_THIRD</InstrId><EndToEndId>TX_THIRD</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">0.21</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 0.21</Ustrd></RmtInf></CdtTrfTxInf></PmtInf></CstmrCdtTrfInitn></Document>
+\ No newline at end of file
diff --git a/libeufin-nexus/sample/platform/maerki_baumann_pain001.xml b/libeufin-nexus/sample/platform/maerki_baumann_pain001.xml
@@ -1 +1 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09 pain.001.001.09.ch.03.xsd"><CstmrCdtTrfInitn><GrpHdr><MsgId>MESSAGE_ID</MsgId><CreDtTm>2024-09-09T00:00:00Z</CreDtTm><NbOfTxs>3</NbOfTxs><CtrlSum>47.32</CtrlSum><InitgPty><Nm>myname</Nm></InitgPty></GrpHdr><PmtInf><PmtInfId>NOTPROVIDED</PmtInfId><PmtMtd>TRF</PmtMtd><BtchBookg>false</BtchBookg><NbOfTxs>3</NbOfTxs><CtrlSum>47.32</CtrlSum><ReqdExctnDt><Dt>2024-09-09Z</Dt></ReqdExctnDt><Dbtr><Nm>myname</Nm></Dbtr><DbtrAcct><Id><IBAN>CH7789144474425692816</IBAN></Id></DbtrAcct><DbtrAgt><FinInstnId><BICFI>BIC</BICFI></FinInstnId></DbtrAgt><ChrgBr>SLEV</ChrgBr><CdtTrfTxInf><PmtId><InstrId>TX_FIRST</InstrId><EndToEndId>TX_FIRST</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">42</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 42</Ustrd></RmtInf></CdtTrfTxInf><CdtTrfTxInf><PmtId><InstrId>TX_SECOND</InstrId><EndToEndId>TX_SECOND</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">5.11</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 5.11</Ustrd></RmtInf></CdtTrfTxInf><CdtTrfTxInf><PmtId><InstrId>TX_THIRD</InstrId><EndToEndId>TX_THIRD</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">0.21</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 0.21</Ustrd></RmtInf></CdtTrfTxInf></PmtInf></CstmrCdtTrfInitn></Document>
-\ No newline at end of file
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09 pain.001.001.09.ch.03.xsd"><CstmrCdtTrfInitn><GrpHdr><MsgId>MESSAGE_ID</MsgId><CreDtTm>2024-09-09T00:00:00Z</CreDtTm><NbOfTxs>3</NbOfTxs><CtrlSum>47.32</CtrlSum><InitgPty><Nm>myname</Nm></InitgPty></GrpHdr><PmtInf><PmtInfId>NOTPROVIDED</PmtInfId><PmtMtd>TRF</PmtMtd><BtchBookg>false</BtchBookg><NbOfTxs>3</NbOfTxs><CtrlSum>47.32</CtrlSum><ReqdExctnDt><Dt>2024-09-09Z</Dt></ReqdExctnDt><Dbtr><Nm>myname</Nm></Dbtr><DbtrAcct><Id><IBAN>CH7789144474425692816</IBAN></Id></DbtrAcct><DbtrAgt><FinInstnId><BICFI>AAAABBCC123</BICFI></FinInstnId></DbtrAgt><ChrgBr>SLEV</ChrgBr><CdtTrfTxInf><PmtId><InstrId>TX_FIRST</InstrId><EndToEndId>TX_FIRST</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">42</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 42</Ustrd></RmtInf></CdtTrfTxInf><CdtTrfTxInf><PmtId><InstrId>TX_SECOND</InstrId><EndToEndId>TX_SECOND</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">5.11</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 5.11</Ustrd></RmtInf></CdtTrfTxInf><CdtTrfTxInf><PmtId><InstrId>TX_THIRD</InstrId><EndToEndId>TX_THIRD</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">0.21</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 0.21</Ustrd></RmtInf></CdtTrfTxInf></PmtInf></CstmrCdtTrfInitn></Document>
+\ No newline at end of file
diff --git a/libeufin-nexus/sample/platform/postfinance_pain001.xml b/libeufin-nexus/sample/platform/postfinance_pain001.xml
@@ -1 +1 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09 pain.001.001.09.ch.03.xsd"><CstmrCdtTrfInitn><GrpHdr><MsgId>MESSAGE_ID</MsgId><CreDtTm>2024-09-09T00:00:00Z</CreDtTm><NbOfTxs>3</NbOfTxs><CtrlSum>47.32</CtrlSum><InitgPty><Nm>myname</Nm></InitgPty></GrpHdr><PmtInf><PmtInfId>NOTPROVIDED</PmtInfId><PmtMtd>TRF</PmtMtd><BtchBookg>false</BtchBookg><NbOfTxs>3</NbOfTxs><CtrlSum>47.32</CtrlSum><ReqdExctnDt><Dt>2024-09-09Z</Dt></ReqdExctnDt><Dbtr><Nm>myname</Nm></Dbtr><DbtrAcct><Id><IBAN>CH7789144474425692816</IBAN></Id></DbtrAcct><DbtrAgt><FinInstnId><BICFI>BIC</BICFI></FinInstnId></DbtrAgt><ChrgBr>SLEV</ChrgBr><CdtTrfTxInf><PmtId><InstrId>TX_FIRST</InstrId><EndToEndId>TX_FIRST</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">42</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 42</Ustrd></RmtInf></CdtTrfTxInf><CdtTrfTxInf><PmtId><InstrId>TX_SECOND</InstrId><EndToEndId>TX_SECOND</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">5.11</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 5.11</Ustrd></RmtInf></CdtTrfTxInf><CdtTrfTxInf><PmtId><InstrId>TX_THIRD</InstrId><EndToEndId>TX_THIRD</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">0.21</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 0.21</Ustrd></RmtInf></CdtTrfTxInf></PmtInf></CstmrCdtTrfInitn></Document>
-\ No newline at end of file
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09 pain.001.001.09.ch.03.xsd"><CstmrCdtTrfInitn><GrpHdr><MsgId>MESSAGE_ID</MsgId><CreDtTm>2024-09-09T00:00:00Z</CreDtTm><NbOfTxs>3</NbOfTxs><CtrlSum>47.32</CtrlSum><InitgPty><Nm>myname</Nm></InitgPty></GrpHdr><PmtInf><PmtInfId>NOTPROVIDED</PmtInfId><PmtMtd>TRF</PmtMtd><BtchBookg>false</BtchBookg><NbOfTxs>3</NbOfTxs><CtrlSum>47.32</CtrlSum><ReqdExctnDt><Dt>2024-09-09Z</Dt></ReqdExctnDt><Dbtr><Nm>myname</Nm></Dbtr><DbtrAcct><Id><IBAN>CH7789144474425692816</IBAN></Id></DbtrAcct><DbtrAgt><FinInstnId><BICFI>AAAABBCC123</BICFI></FinInstnId></DbtrAgt><ChrgBr>SLEV</ChrgBr><CdtTrfTxInf><PmtId><InstrId>TX_FIRST</InstrId><EndToEndId>TX_FIRST</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">42</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 42</Ustrd></RmtInf></CdtTrfTxInf><CdtTrfTxInf><PmtId><InstrId>TX_SECOND</InstrId><EndToEndId>TX_SECOND</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">5.11</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 5.11</Ustrd></RmtInf></CdtTrfTxInf><CdtTrfTxInf><PmtId><InstrId>TX_THIRD</InstrId><EndToEndId>TX_THIRD</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">0.21</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 0.21</Ustrd></RmtInf></CdtTrfTxInf></PmtInf></CstmrCdtTrfInitn></Document>
+\ No newline at end of file
diff --git a/libeufin-nexus/sample/platform/raiffeisen_pain001.xml b/libeufin-nexus/sample/platform/raiffeisen_pain001.xml
@@ -1 +1 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09 pain.001.001.09.ch.03.xsd"><CstmrCdtTrfInitn><GrpHdr><MsgId>MESSAGE_ID</MsgId><CreDtTm>2024-09-09T00:00:00Z</CreDtTm><NbOfTxs>3</NbOfTxs><CtrlSum>47.32</CtrlSum><InitgPty><Nm>myname</Nm></InitgPty></GrpHdr><PmtInf><PmtInfId>NOTPROVIDED</PmtInfId><PmtMtd>TRF</PmtMtd><BtchBookg>false</BtchBookg><NbOfTxs>3</NbOfTxs><CtrlSum>47.32</CtrlSum><ReqdExctnDt><Dt>2024-09-09Z</Dt></ReqdExctnDt><Dbtr><Nm>myname</Nm></Dbtr><DbtrAcct><Id><IBAN>CH7789144474425692816</IBAN></Id></DbtrAcct><DbtrAgt><FinInstnId><BICFI>BIC</BICFI></FinInstnId></DbtrAgt><ChrgBr>SLEV</ChrgBr><CdtTrfTxInf><PmtId><InstrId>TX_FIRST</InstrId><EndToEndId>TX_FIRST</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">42</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 42</Ustrd></RmtInf></CdtTrfTxInf><CdtTrfTxInf><PmtId><InstrId>TX_SECOND</InstrId><EndToEndId>TX_SECOND</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">5.11</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 5.11</Ustrd></RmtInf></CdtTrfTxInf><CdtTrfTxInf><PmtId><InstrId>TX_THIRD</InstrId><EndToEndId>TX_THIRD</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">0.21</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 0.21</Ustrd></RmtInf></CdtTrfTxInf></PmtInf></CstmrCdtTrfInitn></Document>
-\ No newline at end of file
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09 pain.001.001.09.ch.03.xsd"><CstmrCdtTrfInitn><GrpHdr><MsgId>MESSAGE_ID</MsgId><CreDtTm>2024-09-09T00:00:00Z</CreDtTm><NbOfTxs>3</NbOfTxs><CtrlSum>47.32</CtrlSum><InitgPty><Nm>myname</Nm></InitgPty></GrpHdr><PmtInf><PmtInfId>NOTPROVIDED</PmtInfId><PmtMtd>TRF</PmtMtd><BtchBookg>false</BtchBookg><NbOfTxs>3</NbOfTxs><CtrlSum>47.32</CtrlSum><ReqdExctnDt><Dt>2024-09-09Z</Dt></ReqdExctnDt><Dbtr><Nm>myname</Nm></Dbtr><DbtrAcct><Id><IBAN>CH7789144474425692816</IBAN></Id></DbtrAcct><DbtrAgt><FinInstnId><BICFI>AAAABBCC123</BICFI></FinInstnId></DbtrAgt><ChrgBr>SLEV</ChrgBr><CdtTrfTxInf><PmtId><InstrId>TX_FIRST</InstrId><EndToEndId>TX_FIRST</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">42</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 42</Ustrd></RmtInf></CdtTrfTxInf><CdtTrfTxInf><PmtId><InstrId>TX_SECOND</InstrId><EndToEndId>TX_SECOND</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">5.11</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 5.11</Ustrd></RmtInf></CdtTrfTxInf><CdtTrfTxInf><PmtId><InstrId>TX_THIRD</InstrId><EndToEndId>TX_THIRD</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">0.21</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 0.21</Ustrd></RmtInf></CdtTrfTxInf></PmtInf></CstmrCdtTrfInitn></Document>
+\ No newline at end of file
diff --git a/libeufin-nexus/sample/platform/valiant_pain001.xml b/libeufin-nexus/sample/platform/valiant_pain001.xml
@@ -1 +1 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09 pain.001.001.09.ch.03.xsd"><CstmrCdtTrfInitn><GrpHdr><MsgId>MESSAGE_ID</MsgId><CreDtTm>2024-09-09T00:00:00Z</CreDtTm><NbOfTxs>3</NbOfTxs><CtrlSum>47.32</CtrlSum><InitgPty><Nm>myname</Nm></InitgPty></GrpHdr><PmtInf><PmtInfId>NOTPROVIDED</PmtInfId><PmtMtd>TRF</PmtMtd><BtchBookg>false</BtchBookg><NbOfTxs>3</NbOfTxs><CtrlSum>47.32</CtrlSum><ReqdExctnDt><Dt>2024-09-09Z</Dt></ReqdExctnDt><Dbtr><Nm>myname</Nm></Dbtr><DbtrAcct><Id><IBAN>CH7789144474425692816</IBAN></Id></DbtrAcct><DbtrAgt><FinInstnId><BICFI>BIC</BICFI></FinInstnId></DbtrAgt><ChrgBr>SLEV</ChrgBr><CdtTrfTxInf><PmtId><InstrId>TX_FIRST</InstrId><EndToEndId>TX_FIRST</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">42</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 42</Ustrd></RmtInf></CdtTrfTxInf><CdtTrfTxInf><PmtId><InstrId>TX_SECOND</InstrId><EndToEndId>TX_SECOND</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">5.11</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 5.11</Ustrd></RmtInf></CdtTrfTxInf><CdtTrfTxInf><PmtId><InstrId>TX_THIRD</InstrId><EndToEndId>TX_THIRD</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">0.21</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 0.21</Ustrd></RmtInf></CdtTrfTxInf></PmtInf></CstmrCdtTrfInitn></Document>
-\ No newline at end of file
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09 pain.001.001.09.ch.03.xsd"><CstmrCdtTrfInitn><GrpHdr><MsgId>MESSAGE_ID</MsgId><CreDtTm>2024-09-09T00:00:00Z</CreDtTm><NbOfTxs>3</NbOfTxs><CtrlSum>47.32</CtrlSum><InitgPty><Nm>myname</Nm></InitgPty></GrpHdr><PmtInf><PmtInfId>NOTPROVIDED</PmtInfId><PmtMtd>TRF</PmtMtd><BtchBookg>false</BtchBookg><NbOfTxs>3</NbOfTxs><CtrlSum>47.32</CtrlSum><ReqdExctnDt><Dt>2024-09-09Z</Dt></ReqdExctnDt><Dbtr><Nm>myname</Nm></Dbtr><DbtrAcct><Id><IBAN>CH7789144474425692816</IBAN></Id></DbtrAcct><DbtrAgt><FinInstnId><BICFI>AAAABBCC123</BICFI></FinInstnId></DbtrAgt><ChrgBr>SLEV</ChrgBr><CdtTrfTxInf><PmtId><InstrId>TX_FIRST</InstrId><EndToEndId>TX_FIRST</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">42</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 42</Ustrd></RmtInf></CdtTrfTxInf><CdtTrfTxInf><PmtId><InstrId>TX_SECOND</InstrId><EndToEndId>TX_SECOND</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">5.11</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 5.11</Ustrd></RmtInf></CdtTrfTxInf><CdtTrfTxInf><PmtId><InstrId>TX_THIRD</InstrId><EndToEndId>TX_THIRD</EndToEndId></PmtId><Amt><InstdAmt Ccy="CHF">0.21</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 0.21</Ustrd></RmtInf></CdtTrfTxInf></PmtInf></CstmrCdtTrfInitn></Document>
+\ No newline at end of file
diff --git a/src/dialect.rs b/src/dialect.rs
@@ -0,0 +1,56 @@
+/*
+* This file is part of LibEuFin.
+* Copyright (C) 2026 Taler Systems S.A.
+
+* LibEuFin is free software; you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License as
+* published by the Free Software Foundation; either version 3, or
+* (at your option) any later version.
+
+* LibEuFin is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General
+* Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+* License along with LibEuFin; see the file COPYING. If not, see
+* <http://www.gnu.org/licenses/>
+*/
+
+use taler_enum_meta::EnumMeta;
+
+/** Supported EBICS standard */
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Standard {
+ /// Swiss Payment Standards
+ SIX,
+ /// German Banking Industry Committee
+ GBIC,
+}
+
+impl Standard {
+ // TODO
+}
+
+/** Supported bank dialects */
+#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumMeta)]
+#[enum_meta(Str)]
+#[allow(non_camel_case_types)]
+pub enum Dialect {
+ valiant,
+ raiffeisen,
+ postfinance,
+ gls,
+ maerki_baumann,
+}
+
+impl Dialect {
+ pub fn standard(&self) -> Standard {
+ match self {
+ Self::valiant | Self::raiffeisen | Self::postfinance | Self::maerki_baumann => {
+ Standard::SIX
+ }
+ Self::gls => Standard::GBIC,
+ }
+ }
+}
diff --git a/src/iso20022/camt.rs b/src/iso20022/camt.rs
@@ -42,6 +42,7 @@ use crate::{
#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumMeta)]
#[enum_meta(Str)]
+#[allow(clippy::upper_case_acronyms)]
enum Kind {
CRDT,
DBIT,
@@ -523,14 +524,14 @@ pub fn parse_camt(xml: &str) -> xml::Result<Vec<AccountTransactions>> {
}
#[cfg(test)]
-mod test {
+pub mod test {
use std::str::FromStr;
- use jiff::civil::Date;
+ use jiff::{Timestamp, civil::Date};
use taler_common::types::{
amount::{Amount, Currency},
iban::IBAN,
- payto::iban_payto,
+ payto::{BankID, IbanPayto, PaytoImpl as _, PaytoURI},
utils::date_to_utc_ts,
};
@@ -539,6 +540,18 @@ mod test {
model::{InId, InTx, OutBatch, OutId, OutReversal, OutTx, Tx},
};
+ pub fn date_to_timestamp(date: &str) -> Timestamp {
+ date_to_utc_ts(&Date::from_str(date).unwrap())
+ }
+
+ fn iban_payto(iban: impl AsRef<str>, name: impl AsRef<str>) -> PaytoURI {
+ IbanPayto::new(BankID {
+ iban: iban.as_ref().parse().expect("invalid IBAN"),
+ bic: None,
+ })
+ .as_full_payto(name.as_ref())
+ }
+
pub fn check_tx(path: &str, iban: &str, currency: Option<&str>, txs: &[Tx]) {
let content = std::fs::read_to_string(path).unwrap();
let res = parse_camt(&content).unwrap();
@@ -570,7 +583,7 @@ mod test {
amount: Amount::from_str(amount).unwrap(),
debit_fee: Amount::from_str(debit_fee).unwrap(),
subject: subject.map(Into::into),
- execution_time: date_to_utc_ts(&Date::from_str(execution_time).unwrap()),
+ execution_time: date_to_timestamp(execution_time),
creditor: creditor.map(|(iban, name)| iban_payto(iban, name)),
})
}
@@ -592,7 +605,7 @@ mod test {
amount: Amount::from_str(amount).unwrap(),
credit_fee: Amount::from_str(credit_fee).unwrap(),
subject: subject.map(Into::into),
- execution_time: date_to_utc_ts(&Date::from_str(execution_time).unwrap()),
+ execution_time: date_to_timestamp(execution_time),
debtor: debtor.map(|(iban, name)| iban_payto(iban, name)),
})
}
@@ -607,14 +620,14 @@ mod test {
e2e_id: e2e_id.parse().unwrap(),
msg_id: msg_id.map(Into::into),
reason: reason.into(),
- execution_time: date_to_utc_ts(&Date::from_str(execution_time).unwrap()),
+ execution_time: date_to_timestamp(execution_time),
})
}
pub fn tx_batch(msg_id: &str, execution_time: &str) -> Tx {
Tx::Batch(OutBatch {
msg_id: msg_id.into(),
- execution_time: date_to_utc_ts(&Date::from_str(execution_time).unwrap()),
+ execution_time: date_to_timestamp(execution_time),
})
}
diff --git a/src/iso20022/mod.rs b/src/iso20022/mod.rs
@@ -22,6 +22,7 @@ use taler_enum_meta::EnumMeta;
pub mod bank_tx_code;
pub mod camt;
pub mod hac;
+pub mod pain001;
pub mod pain002;
pub mod status_code;
diff --git a/src/iso20022/pain001.rs b/src/iso20022/pain001.rs
@@ -0,0 +1,253 @@
+/*
+* This file is part of LibEuFin.
+* Copyright (C) 2026 Taler Systems S.A.
+
+* LibEuFin is free software; you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License as
+* published by the Free Software Foundation; either version 3, or
+* (at your option) any later version.
+
+* LibEuFin is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General
+* Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+* License along with LibEuFin; see the file COPYING. If not, see
+* <http://www.gnu.org/licenses/>
+*/
+
+use anyhow::bail;
+use compact_str::CompactString;
+use jiff::{Timestamp, Zoned, tz::TimeZone};
+use taler_common::types::{
+ amount::{Amount, Decimal},
+ payto::FullIbanPayto,
+};
+
+use crate::{
+ dialect::{Dialect, Standard},
+ xml::XmlWriter,
+ xml_build, xml_el,
+};
+
+/** pain.001 transaction metadata */
+pub struct Pain001Tx {
+ pub creditor: FullIbanPayto,
+ pub amount: Amount,
+ pub subject: Box<str>,
+ pub e2e_id: CompactString,
+}
+
+/** pain.001 message metadata */
+pub struct Pain001Msg {
+ pub msg_id: CompactString,
+ pub timestamp: Timestamp,
+ pub debtor: FullIbanPayto,
+ pub sum: Amount,
+ pub txs: Box<[Pain001Tx]>,
+}
+
+/** Check EBICS compability of an amount */
+fn ebics_amount(amount: Amount) -> anyhow::Result<Decimal> {
+ if amount.is_sub_cent() {
+ bail!("Sub-cent amounts not supported")
+ }
+ Ok(amount.decimal())
+}
+
+/** Create a pain.001 XML document [msg] valid for [dialect] */
+pub fn create_pain001(
+ msg: &Pain001Msg,
+ dialect: &Dialect,
+ instant: bool,
+) -> anyhow::Result<String> {
+ let version = "09";
+ let suffix = match dialect.standard() {
+ Standard::SIX => ".ch.03",
+ Standard::GBIC => "",
+ };
+ let total = ebics_amount(msg.sum)?;
+ Ok(xml_build!(
+ "Document"
+ ("xmlns": (format_args!("urn:iso:std:iso:20022:tech:xsd:pain.001.001.{version}")))
+ ("xmlns:xsi": (format_args!("http://www.w3.org/2001/XMLSchema-instance")))
+ ("xsi:schemaLocation": (format_args!("urn:iso:std:iso:20022:tech:xsd:pain.001.001.{version} pain.001.001.{version}{suffix}.xsd")))
+ {
+ "CstmrCdtTrfInitn" {
+ "GrpHdr" {
+ // Used for idempotency as banks will refuse to process EBICS request with the same MsgId for a pre-agreed period
+ // Used to uniquely identify batches of transactions in other files
+ "MsgId": msg.msg_id,
+ "CreDtTm": msg.timestamp,
+ "NbOfTxs": msg.txs.len(),
+ "CtrlSum": total,
+ "InitgPty" {
+ "Nm": msg.debtor.name
+ }/*
+ // TODO fail with GLS: ES_VERIFICATION IncorrectFileStructure - 'Signature verification' 'The file format is incomplete or invalid'
+ "InitnSrc" {
+ "Nm": "LibEuFin",
+ "Prvdr": "Taler Systems SA",
+ "Vrsn": taler_build::long_version()
+ }*/
+ },
+ "PmtInf" {
+ "PmtInfId": "NOTPROVIDED",
+ "PmtMtd": "TRF",
+ "BtchBookg": "false",
+ "NbOfTxs": msg.txs.len(),
+ "CtrlSum": total,
+ @ |w: &mut XmlWriter| {
+ if dialect.standard() == Standard::GBIC {
+ xml_el!(w, "PmtTpInf" {
+ "SvcLvl" {
+ "Cd": "SEPA"
+ },
+ @ |w: &mut XmlWriter| {
+ if instant {
+ xml_el!(w, "LclInstrm" {
+ "Cd": "INST"
+ });
+ }
+ }
+ });
+ }
+ },
+ "ReqdExctnDt" {
+ "Dt": Zoned::new(msg.timestamp, TimeZone::UTC).date().to_string() + "Z"
+ },
+ "Dbtr" {
+ "Nm": msg.debtor.name
+ },
+ "DbtrAcct" {
+ "Id" {
+ "IBAN": msg.debtor.iban
+ }
+ },
+ "DbtrAgt" {
+ "FinInstnId" {
+ @ |w: &mut XmlWriter| {
+ if let Some(bic) = &msg.debtor.bic {
+ xml_el!(w, "BICFI": bic);
+ } else {
+ xml_el!(w, "Othr" {"Id": "NOTPROVIDED"});
+ }
+ }
+ }
+
+ },
+ "ChrgBr": "SLEV",
+ @ |w: &mut XmlWriter| {
+ for tx in &msg.txs {
+ xml_el!(w, "CdtTrfTxInf" {
+ "PmtId" {
+ "InstrId": tx.e2e_id,
+ // Used to uniquely identify transactions in other files
+ "EndToEndId": tx.e2e_id
+ },
+ "Amt" {
+ "InstdAmt" ("Ccy": (tx.amount.currency)): ebics_amount(tx.amount).unwrap()
+ },
+ @ |w: &mut XmlWriter| {
+ if let Some(bic) = &tx.creditor.bic {
+ xml_el!(w, "CdtrAgt" {
+ "FinInstnId" {
+ "BICFI": bic
+ }
+ });
+ }
+ },
+ "Cdtr" {
+ "Nm": tx.creditor.name
+ // Addr might become a requirement in the future
+ /*"PstlAdr" {
+ "TwnNm": "Bochum",
+ "Ctry": "DE"
+ }*/
+ },
+ "CdtrAcct" {
+ "Id" {
+ "IBAN": tx.creditor.iban
+ }
+ },
+ "RmtInf" {
+ "Ustrd": tx.subject
+ }
+ });
+ }
+ }
+ }
+ }
+ }
+ ))
+}
+
+#[cfg(test)]
+mod test {
+ use taler_common::types::{
+ amount::amount,
+ payto::{BankID, FullIbanPayto},
+ };
+
+ use crate::{
+ dialect::Dialect,
+ iso20022::{
+ camt::test::date_to_timestamp,
+ pain001::{Pain001Msg, Pain001Tx, create_pain001},
+ },
+ };
+
+ #[test]
+ fn pain001() {
+ let creditor = FullIbanPayto::new(
+ BankID {
+ iban: "CH4189144589712575493".parse().expect("invalid IBAN"),
+ bic: None,
+ },
+ "Test",
+ );
+
+ let msg = Pain001Msg {
+ msg_id: "MESSAGE_ID".into(),
+ timestamp: date_to_timestamp("2024-09-09"),
+ debtor: FullIbanPayto::new(
+ BankID {
+ iban: "CH7789144474425692816".parse().expect("invalid IBAN"),
+ bic: Some("AAAABBCC123".parse().expect("invalid BIC")),
+ },
+ "myname",
+ ),
+ sum: amount("CHF:47.32"),
+ txs: Box::new([
+ Pain001Tx {
+ creditor: creditor.clone(),
+ amount: amount("CHF:42"),
+ subject: "Test 42".into(),
+ e2e_id: "TX_FIRST".into(),
+ },
+ Pain001Tx {
+ creditor: creditor.clone(),
+ amount: amount("CHF:5.11"),
+ subject: "Test 5.11".into(),
+ e2e_id: "TX_SECOND".into(),
+ },
+ Pain001Tx {
+ creditor: creditor,
+ amount: amount("CHF:0.21"),
+ subject: "Test 0.21".into(),
+ e2e_id: "TX_THIRD".into(),
+ },
+ ]),
+ };
+ for dialect in Dialect::entries {
+ pretty_assertions::assert_eq!(
+ std::fs::read_to_string(format!(
+ "libeufin-nexus/sample/platform/{dialect}_pain001.xml"
+ ))
+ .unwrap(),
+ create_pain001(&msg, dialect, false).unwrap()
+ );
+ }
+ }
+}
diff --git a/src/iso20022/pain002.rs b/src/iso20022/pain002.rs
@@ -135,7 +135,7 @@ pub fn parse_pain002(xml: &str) -> xml::Result<MsgStatus> {
.collect()
}
- return Xml::parse_str(xml, "Document", |root| {
+ Xml::parse_str(xml, "Document", |root| {
let n = root.one("CstmrPmtStsRpt")?;
let status = n.one("OrgnlGrpInfAndSts")?;
Ok(MsgStatus {
@@ -164,7 +164,7 @@ pub fn parse_pain002(xml: &str) -> xml::Result<MsgStatus> {
})
.collect::<xml::Result<_>>()?,
})
- });
+ })
}
#[cfg(test)]
diff --git a/src/key_management.rs b/src/key_management.rs
@@ -155,6 +155,12 @@ pub async fn key_management(
_ebics_logger: &EbicsLogger,
order: Order,
) -> anyhow::Result<EbicsResponse<Option<String>>> {
+ let EbicsHostCfg {
+ host_id,
+ user_id,
+ partner_id,
+ ..
+ } = cfg;
info!("Doing key request {}", order.name());
//val txLog = ebicsLogger.tx(order.name)
// TODO is this still necessary ?
@@ -201,7 +207,7 @@ pub async fn key_management(
let b64 = BASE64_STANDARD.encode(der.as_ref());
xml_el!(w, "ds:X509Data" {
- "ds:X509Certificate": &b64
+ "ds:X509Certificate": b64
});
}
let data = match order {
@@ -243,18 +249,18 @@ pub async fn key_management(
{
"header" ("authenticate": "true") {
"static" {
- "HostID": &cfg.host_id,
+ "HostID": host_id,
@ |w: &mut XmlWriter| {
if order == Order::HPB {
let nonce: u128 = rand::random();
xml_el!(w,
- "Nonce": &format_args!("{:032x}", nonce),
- "Timestamp": &jiff::Timestamp::now()
+ "Nonce": format_args!("{:032x}", nonce),
+ "Timestamp": jiff::Timestamp::now()
);
}
},
- "PartnerID": &cfg.partner_id,
- "UserID": &cfg.user_id,
+ "PartnerID": partner_id,
+ "UserID": user_id,
"OrderDetails" {
"AdminOrderType": order.name()
},
@@ -270,14 +276,13 @@ pub async fn key_management(
"body" {
@ |w: &mut XmlWriter| {
if let Some(data) = data {
- xml_el!(w,"DataTransfer" {
+ xml_el!(w, "DataTransfer" {
"OrderData": data
});
}
}
}
}
-
);
let signed = if sign {
sign_ebics(msg, &client.authentication_private_key)
diff --git a/src/lib.rs b/src/lib.rs
@@ -45,6 +45,7 @@ pub mod common;
pub mod config;
pub mod crypto;
pub mod db;
+pub mod dialect;
pub mod ebics_code;
pub mod iso20022;
pub mod key_management;
diff --git a/src/xml.rs b/src/xml.rs
@@ -17,49 +17,48 @@
* <http://www.gnu.org/licenses/>
*/
-use std::{fmt::Display, str::FromStr};
+use std::{
+ fmt::{Display, Write},
+ str::FromStr,
+};
use base64::{Engine, prelude::BASE64_STANDARD};
-use jiff::Timestamp;
use roxmltree::{Document, Node};
#[macro_export]
macro_rules! xml_el {
+ // Logic escape
+ ($w:ident, @ $logic:expr$(, $($rest:tt)*)?) => {
+ ($logic)($w);
+ $($crate::xml_el!($w, $($rest)*);)*
+ };
// Text element
($w:ident, $name:tt $(($k:tt: $v:tt))* : $content:expr $(, $($rest:tt)*)?) => {
- $w.text($name, &[$(($k, $v)),*], $content);
+ $w.text(&$name, &[$((&$k, &$v)),*], &$content);
$($crate::xml_el!($w, $($rest)*);)*
};
// Nested block
($w:ident, $name:tt $(($k:tt: $v:tt))* { $($body:tt)* }$(, $($rest:tt)*)?) => {
- let name = $name;
- $w.open(name, &[$(($k, $v)),*]);
+ let name = &$name;
+ $w.open(&name, &[$((&$k, &$v)),*]);
$crate::xml_el!($w, $($body)*);
- $w.close(name);
+ $w.close(&name);
$($crate::xml_el!($w, $($rest)*);)*
};
// Empty element
($w:ident, $name:tt $(($k:tt: $v:tt))* $(, $($rest:tt)*)?) => {
- $w.empty($name, &[$(($k, $v)),*]);
- $($crate::xml_el!($w, $($rest)*);)*
- };
- // Logic escape
- ($w:ident, @ $logic:expr$(, $($rest:tt)*)?) => {
- ($logic)($w);
+ $w.empty(&$name, &[$((&$k, &$v)),*]);
$($crate::xml_el!($w, $($rest)*);)*
};
}
#[macro_export]
macro_rules! xml_build {
- ($name:tt $(($k:tt: $v:tt))* { $($body:tt)* }) => {
+ ($($xml:tt)*) => {
{
let mut writer = $crate::xml::XmlWriter::init();
let w = &mut writer;
- let name = $name;
- w.open(name, &[$(($k, $v)),*]);
- $crate::xml_el!(w, $($body)*);
- w.close(name);
+ $crate::xml_el!(w, $($xml)*);
writer.finish()
}
};
@@ -74,43 +73,48 @@ impl XmlWriter {
Self { buff }
}
- pub fn open(&mut self, name: &str, attrs: &[(&str, &str)]) {
+ pub fn open(&mut self, name: &dyn Display, attrs: &[(&dyn Display, &dyn Display)]) {
self.buff.push('<');
self.write_tag_attrs(name, attrs);
self.buff.push('>');
}
- pub fn close(&mut self, name: &str) {
+ pub fn close(&mut self, name: &dyn Display) {
self.buff.push_str("</");
- self.buff.push_str(name);
+ self.buff.write_fmt(format_args!("{name}")).unwrap();
self.buff.push('>');
}
- pub fn empty(&mut self, name: &str, attrs: &[(&str, &str)]) {
+ pub fn empty(&mut self, name: &dyn Display, attrs: &[(&dyn Display, &dyn Display)]) {
self.buff.push('<');
self.write_tag_attrs(name, attrs);
self.buff.push_str("/>");
}
- pub fn text<D: Display>(&mut self, name: &str, attrs: &[(&str, &str)], content: D) {
+ pub fn text(
+ &mut self,
+ name: &dyn Display,
+ attrs: &[(&dyn Display, &dyn Display)],
+ content: &dyn Display,
+ ) {
self.open(name, attrs);
self.write_escaped(content);
self.close(name);
}
- fn write_tag_attrs(&mut self, name: &str, attrs: &[(&str, &str)]) {
- self.buff.push_str(name);
+ fn write_tag_attrs(&mut self, name: &dyn Display, attrs: &[(&dyn Display, &dyn Display)]) {
+ self.buff.write_fmt(format_args!("{name}")).unwrap();
for (key, value) in attrs {
self.buff.push(' ');
- self.buff.push_str(key);
+ self.buff.write_fmt(format_args!("{key}")).unwrap();
self.buff.push_str("=\"");
- self.write_escaped(value);
+ self.write_escaped(*value);
self.buff.push('"');
}
}
- fn write_escaped<D: Display>(&mut self, content: D) {
+ fn write_escaped(&mut self, content: &dyn Display) {
std::fmt::write(self, format_args!("{content}")).unwrap();
}
@@ -422,8 +426,8 @@ mod test {
"endOfDocument" {
@ |w: &mut XmlWriter| {
for i in 1..=10 {
- xml_el!(w, (&format!("e{i}")) {
- (&format!("e{i}{i}")): &format_args!("{i}{i}{i}")
+ xml_el!(w, (format_args!("e{i}")) {
+ (format_args!("e{i}{i}")): (format_args!("{i}{i}{i}"))
});
}
}