├── .github ├── FUNDING.yml └── workflows │ ├── clojure.yml │ ├── coverage.yml │ └── rust.yml ├── .gitignore ├── Cargo.toml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── benches ├── both_ways.rs ├── crc16.rs ├── parse.rs └── to_brcode.rs ├── clj-brcode ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── project.clj ├── src │ └── clj_brcode │ │ └── core.clj └── test │ └── clj_brcode │ └── core_test.clj ├── dartbrcode ├── CHANGELOG.md ├── LICENSE ├── README.md ├── lib │ └── dartbrcode.dart ├── pubspec.lock ├── pubspec.yaml └── test │ └── lib.dart ├── jvm-brcode ├── .gitattributes ├── .gitignore ├── Makefile ├── README.md ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ ├── main │ └── java │ │ └── jvm │ │ └── brcode │ │ └── Brcode.java │ └── test │ └── java │ └── jvm │ └── brcode │ └── BrcodeTest.java ├── libbrcode.dylib ├── libbrcode.so ├── src ├── aux.rs ├── emit.rs ├── lib.rs ├── model.rs └── parse.rs └── tests └── lib.rs /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | 2 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 3 | patreon: naomijub 4 | custom: ["https://github.com/sponsors/naomijub", "https://patreon.com/naomijub"] 5 | -------------------------------------------------------------------------------- /.github/workflows/clojure.yml: -------------------------------------------------------------------------------- 1 | name: Clojure CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Install dependencies 17 | run: | 18 | cd clj-brcode 19 | lein deps 20 | - name: Run tests 21 | run: | 22 | cd clj-brcode 23 | curl -s https://api.github.com/repos/naomijub/brcode/releases/latest \ 24 | | grep "browser_download_url.*so" \ 25 | | cut -d : -f 2,3 \ 26 | | tr -d \" \ 27 | | wget -qi - 28 | export LD_LIBRARY_PATH=. 29 | lein test 30 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: coverage 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | test: 11 | name: coverage 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout repository 16 | uses: actions/checkout@v2 17 | 18 | - name: Generate code coverage 19 | run: | 20 | cargo install cargo-tarpaulin 21 | cargo tarpaulin --verbose --all-features --workspace --timeout 300 --out Xml 22 | 23 | - name: Upload to codecov.io 24 | uses: codecov/codecov-action@v1 25 | with: 26 | token: ${{secrets.CODECOV_TOKEN}} 27 | fail_ci_if_error: true -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build-ubuntu: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Build 20 | run: cargo build --release 21 | - name: Run tests 22 | run: cargo test --release --verbose 23 | 24 | build-macos: 25 | 26 | runs-on: macos-latest 27 | 28 | steps: 29 | - uses: actions/checkout@v2 30 | - name: Build 31 | run: cargo build --release 32 | - name: Run tests 33 | run: cargo test --release --verbose 34 | 35 | clippy: 36 | runs-on: ubuntu-latest 37 | 38 | steps: 39 | - uses: actions/checkout@v2 40 | 41 | - name: cache-build-artifacts 42 | uses: actions/cache@v2 43 | with: 44 | path: target 45 | key: cargo-clippy-${{ hashFiles('Cargo.lock') }} 46 | restore-keys: | 47 | cargo-clippy-${{ hashFiles('Cargo.lock') }} 48 | cargo-clippy 49 | - name: install-clippy 50 | run: rustup component add clippy 51 | 52 | - name: run-clippy 53 | run: | 54 | sudo apt-get install -y libudev-dev 55 | cargo clippy -- -W clippy::pedantic 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | /out 4 | .DS_Store 5 | 6 | */target 7 | */classes 8 | */checkouts 9 | */profiles.clj 10 | */pom.xml 11 | */pom.xml.asc 12 | */*.jar 13 | */**.class 14 | */.lein-* 15 | */.nrepl-port 16 | */.hgignore 17 | */.hg/ 18 | */node_modules 19 | */.dart_tool/ 20 | */.packages 21 | */.calva/ 22 | */.idea -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "brcode" 3 | version = "1.4.3" 4 | authors = ["Julia Naomi "] 5 | edition = "2018" 6 | description = "Crate to parse and emit BR Codes" 7 | readme = "README.md" 8 | documentation = "https://docs.rs/brcode/" 9 | repository = "https://github.com/naomijub/brcode" 10 | keywords = ["BRCode", "QRCode", "EMV"] 11 | license = "LGPL-3.0" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [lib] 16 | name = "brcode" 17 | path = "src/lib.rs" 18 | crate-type = ["rlib", "dylib"] 19 | 20 | [dependencies] 21 | edn-rs = "0.15.0" 22 | edn-derive = "0.4.2" 23 | serde = { version = "1.0", features = ["derive"] } 24 | serde_json = "1.0" 25 | serde_derive = "1" 26 | qrcode-generator = "4.0.3" 27 | 28 | [dev-dependencies] 29 | criterion = "0.3" 30 | 31 | [[bench]] 32 | name = "parse" 33 | harness = false 34 | 35 | [[bench]] 36 | name = "to_brcode" 37 | harness = false 38 | 39 | [[bench]] 40 | name = "both_ways" 41 | harness = false 42 | 43 | [[bench]] 44 | name = "crc16" 45 | harness = false -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:latest 2 | 3 | COPY Cargo.* ./ 4 | COPY src/ ./src 5 | COPY benches/ ./benches 6 | 7 | RUN cargo build --release 8 | RUN cat target/release/libbrcode.so > libbrcode.so 9 | ENTRYPOINT [ "/bin/bash" ] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. [http://fsf.org/] 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build-macos: 2 | cargo build --release 3 | 4 | cp-macos: 5 | cp target/release/libbrcode.dylib ./ 6 | cp target/release/libbrcode.dylib clj-brcode/ 7 | cp target/release/libbrcode.dylib dartbrcode/ 8 | cp target/release/libbrcode.dylib jvm-brcode/ 9 | 10 | build-linux: 11 | cargo build --release 12 | 13 | cp-linux: 14 | cp target/release/libbrcode.so ./ 15 | cp target/release/libbrcode.so clj-brcode/ 16 | cp target/release/libbrcode.so dartbrcode/ 17 | cp target/release/libbrcode.so jvm-brcode/ 18 | 19 | build: build-macos 20 | DOCKER_BUILDKIT=1 docker build --file Dockerfile --output out . || cp out/libbrcode.so ./ 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BR Code 2 | [![codecov](https://codecov.io/gh/naomijub/brcode/branch/master/graph/badge.svg?token=1DEONNFLTX)](https://codecov.io/gh/naomijub/brcode) 3 | 4 | A crate to parse and emit [PIX BR Code](https://www.bcb.gov.br/content/estabilidadefinanceira/spb_docs/ManualBRCode.pdf). 5 | * [Technical and Business specs for BR Code usage](https://www.bcb.gov.br/content/estabilidadefinanceira/forumpireunioes/Anexo%20I%20-%20QRCodes%20-%20Especifica%C3%A7%C3%A3o%20-%20vers%C3%A3o%201-1.pdf) 6 | 7 | ## Important Changes 8 | 9 | * Version `1.2` has a small break for a `BrCode` field. [PR](https://github.com/naomijub/brcode/pull/14) fixes `model::BrCode` field `initiation_method` naming. 10 | 11 | ## Usage 12 | 13 | ```toml 14 | [dependencies] 15 | brcode = "1.4.2" 16 | ``` 17 | 18 | ### Build from source 19 | 1. Install [rustup](https://rustup.rs/). 20 | 2. `make build-macos` for macos and ios files or `make build-linux` for linux and android files. 21 | 3. Files will be located at `target/release/libbrcode.*`, `target//release/libbrcode.so`. 22 | 4. Copy them to the root of your project. 23 | 5. For JVM it is important to run `export LD_LIBRARY_PATH=.` at the root of the project 24 | 25 | ### Copy files from Github Release 26 | Shellscript to get files from release: 27 | 28 | **So** 29 | ```sh 30 | curl -s https://api.github.com/repos/naomijub/brcode/releases/latest \ 31 | | grep "browser_download_url.*so" \ 32 | | cut -d : -f 2,3 \ 33 | | tr -d \" \ 34 | | wget -qi - 35 | ``` 36 | 37 | **dylib** 38 | ```sh 39 | curl -s https://api.github.com/repos/naomijub/brcode/releases/latest \ 40 | | grep "browser_download_url.*dylib" \ 41 | | cut -d : -f 2,3 \ 42 | | tr -d \" \ 43 | | wget -qi - 44 | ``` 45 | 46 | ## Example 47 | 48 | **Parse String** 49 | ```rust 50 | use brcode::{from_str, Data}; 51 | 52 | fn main() { 53 | let code = "00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304AD38"; 54 | 55 | assert_eq!(from_str(code), expected()); 56 | } 57 | 58 | fn expected() -> Vec<(usize, Data)> { 59 | vec![ 60 | (0, Data::Single("01".to_string())), 61 | (4, Data::Single("12345678901234".to_string())), 62 | (26, Data::Vector(vec![ 63 | (0, Data::Single("BR.GOV.BCB.PIX".to_string())), 64 | (1, Data::Single("123e4567-e12b-12d1-a456-426655440000".to_string()))])), 65 | (27, Data::Vector(vec![ 66 | (0, Data::Single("BR.COM.OUTRO".to_string())), 67 | (1, Data::Single("0123456789".to_string()))])), 68 | (52, Data::Single("0000".to_string())), 69 | (53, Data::Single("986".to_string())), 70 | (54, Data::Single("123.45".to_string())), 71 | (58, Data::Single("BR".to_string())), 72 | (59, Data::Single("NOME DO RECEBEDOR".to_string())), 73 | (60, Data::Single("BRASILIA".to_string())), 74 | (61, Data::Single("70074900".to_string())), 75 | (62, Data::Vector(vec![ 76 | (5, Data::Single("RP12345678-2019".to_string()))])), 77 | (80, Data::Vector(vec![( 78 | 0, Data::Single("BR.COM.OUTRO".to_string())), 79 | (1, Data::Single("0123.ABCD.3456.WXYZ".to_string()))])), 80 | (63, Data::Single("AD38".to_string()))] 81 | } 82 | ``` 83 | 84 | **str_to_brcode** 85 | ```rust 86 | use brcode::{str_to_brcode, BrCode, Template, Info, MerchantInfo, Label}; 87 | 88 | 89 | fn main() { 90 | let code = "00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304AD38"; 91 | 92 | assert_eq!(from_str(code), expected()); 93 | } 94 | 95 | fn expected() -> BrCode { 96 | BrCode { 97 | payload_version: 1, 98 | initiation_method: None, 99 | merchant_category_code: 0000u32, 100 | merchant_name: "NOME DO RECEBEDOR".to_string(), 101 | merchant_city: "BRASILIA".to_string(), 102 | merchant_information: vec![ 103 | MerchantInfo { 104 | id: 26, 105 | info: vec![ 106 | Info { 107 | id: 0, 108 | info: "BR.GOV.BCB.PIX".to_string(), 109 | }, 110 | Info { 111 | id: 1, 112 | info: "123e4567-e12b-12d1-a456-426655440000".to_string(), 113 | }, 114 | ], 115 | }, 116 | MerchantInfo { 117 | id: 27, 118 | info: vec![ 119 | Info { 120 | id: 0, 121 | info: "BR.COM.OUTRO".to_string(), 122 | }, 123 | Info { 124 | id: 1, 125 | info: "0123456789".to_string(), 126 | }, 127 | ], 128 | }, 129 | ], 130 | currency: "986".to_string(), 131 | postal_code: Some("70074900".to_string()), 132 | amount: Some(123.45), 133 | country_code: "BR".to_string(), 134 | field_template: vec![Label { 135 | reference_label: "RP12345678-2019".to_string(), 136 | }], 137 | crc1610: "AD38".to_string(), 138 | templates: Some(vec![Template { 139 | id: 80usize, 140 | info: vec![ 141 | Info { 142 | id: 0usize, 143 | info: "BR.COM.OUTRO".to_string(), 144 | }, 145 | Info { 146 | id: 1usize, 147 | info: "0123.ABCD.3456.WXYZ".to_string(), 148 | }, 149 | ], 150 | }]), 151 | } 152 | } 153 | ``` 154 | 155 | **brcode::to_string** from `Vec<(usize, Data)>`: 156 | ```rust 157 | use brcode::{ 158 | self, BrCode, 159 | }; 160 | 161 | fn main() { 162 | let actual = brcode::to_string(brcode_vec()); 163 | let code = "00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304AD38"; 164 | 165 | assert_eq!(actual, code); 166 | } 167 | 168 | fn brcode_vec() -> Vec<(usize, Data)> { 169 | vec![ 170 | (0, Data::Single("01".to_string())), 171 | (4, Data::Single("12345678901234".to_string())), 172 | ( 173 | 26, 174 | Data::Vector(vec![ 175 | (0, Data::Single("BR.GOV.BCB.PIX".to_string())), 176 | ( 177 | 1, 178 | Data::Single("123e4567-e12b-12d1-a456-426655440000".to_string()), 179 | ), 180 | ]), 181 | ), 182 | ( 183 | 27, 184 | Data::Vector(vec![ 185 | (0, Data::Single("BR.COM.OUTRO".to_string())), 186 | (1, Data::Single("0123456789".to_string())), 187 | ]), 188 | ), 189 | (52, Data::Single("0000".to_string())), 190 | (53, Data::Single("986".to_string())), 191 | (54, Data::Single("123.45".to_string())), 192 | (58, Data::Single("BR".to_string())), 193 | (59, Data::Single("NOME DO RECEBEDOR".to_string())), 194 | (60, Data::Single("BRASILIA".to_string())), 195 | (61, Data::Single("70074900".to_string())), 196 | ( 197 | 62, 198 | Data::Vector(vec![(5, Data::Single("RP12345678-2019".to_string()))]), 199 | ), 200 | ( 201 | 80, 202 | Data::Vector(vec![ 203 | (0, Data::Single("BR.COM.OUTRO".to_string())), 204 | (1, Data::Single("0123.ABCD.3456.WXYZ".to_string())), 205 | ]), 206 | ), 207 | (63, Data::Single("AD38".to_string())), 208 | ] 209 | } 210 | 211 | ``` 212 | 213 | **brcode::brcode_to_string** for struct `BrCode`: 214 | ```rust 215 | use brcode::{ 216 | self, BrCode, 217 | }; 218 | 219 | fn main() { 220 | let actual = brcode::brcode_to_string(brcode_value()); 221 | let code = "00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304AD38"; 222 | 223 | assert_eq!(actual, code); 224 | } 225 | 226 | fn brcode_value() -> BrCode { 227 | BrCode { 228 | payload_version: 1, 229 | initiation_method: None, 230 | merchant_account_information: Some(String::from("12345678901234")), 231 | merchant_category_code: 0000u32, 232 | merchant_name: "NOME DO RECEBEDOR".to_string(), 233 | merchant_city: "BRASILIA".to_string(), 234 | merchant_information: vec![ 235 | MerchantInfo { 236 | id: 26, 237 | info: vec![ 238 | Info { 239 | id: 0, 240 | info: "BR.GOV.BCB.PIX".to_string(), 241 | }, 242 | Info { 243 | id: 1, 244 | info: "123e4567-e12b-12d1-a456-426655440000".to_string(), 245 | }, 246 | ], 247 | }, 248 | MerchantInfo { 249 | id: 27, 250 | info: vec![ 251 | Info { 252 | id: 0, 253 | info: "BR.COM.OUTRO".to_string(), 254 | }, 255 | Info { 256 | id: 1, 257 | info: "0123456789".to_string(), 258 | }, 259 | ], 260 | }, 261 | ], 262 | currency: "986".to_string(), 263 | postal_code: Some("70074900".to_string()), 264 | amount: Some(123.45), 265 | country_code: "BR".to_string(), 266 | field_template: vec![Label { 267 | reference_label: "RP12345678-2019".to_string(), 268 | }], 269 | crc1610: "AD38".to_string(), 270 | templates: Some(vec![Template { 271 | id: 80usize, 272 | info: vec![ 273 | Info { 274 | id: 0usize, 275 | info: "BR.COM.OUTRO".to_string(), 276 | }, 277 | Info { 278 | id: 1usize, 279 | info: "0123.ABCD.3456.WXYZ".to_string(), 280 | }, 281 | ], 282 | }]), 283 | } 284 | } 285 | ``` 286 | 287 | ### **`BrCode` functions** context related 288 | * `pub fn is_pix(&self) -> bool` determines if BrCode is a `PIX` transaction. 289 | * `pub fn get_transaction_id(&self) -> Option` gets `transaction_id` value (field 5 of item 65). 290 | * `pub fn get_alias(&self) -> Option>` gets all possible values for PIX aliases. Usually only field 1 of item 26 is valid. Checks if the alias if of type `"BR.GOV.BCB.PIX"`. 291 | * `pub fn get_message(&self) -> Option>` gets all possible massages for PIX aliases. Usually only field 2 of item 26 is valid. Checks if the alias if of type `"BR.GOV.BCB.PIX"`. 292 | 293 | ### **`BrCode` functions** QrCode generation related: 294 | * `to_svg_string(&self, ecc: QrCodeEcc, size: usize) -> String` generates a SVG xml in a String. 295 | * `to_svg_standard_string(&self) -> String` generates a SVG xml in a String with `QrCodeEcc::Low` and `size = 1024`. 296 | * `to_vec_u8(&self, ecc: QrCodeEcc, size: usize) -> Vec` generates a PNG formatted in `Vec`. 297 | * `to_svg_file(&self, file_path: &str, ecc: QrCodeEcc, size: usize)` creates a QR Code in format `.svg` located at file path `file_path`. 298 | * `to_standard_svg_file(&self, file_path: &str)` creates a QR Code in format `.svg` located at file path `file_path` with `QrCodeEcc::Low` and `size = 1024`. 299 | * `to_png_file(&self, file_path: &str, ecc: QrCodeEcc, size: usize)` creates a QR Code in format `.png` located at file path `file_path`. 300 | 301 | * `file_path` examples are `tests/data/file_output.png` and `tests/data/file_output.svg` from the project root. If directory doesn't exist an error will be thrown. 302 | * `QrCodeEcc` is the error correction level in a QR Code symbol and `size` is the image size. 303 | * `QrCodeEcc::Low` The QR Code can tolerate about 7% erroneous codewords. 304 | * `QrCodeEcc::Medium` The QR Code can tolerate about 15% erroneous codewords. 305 | * `QrCodeEcc::Quartile` The QR Code can tolerate about 25% erroneous codewords. 306 | * `QrCodeEcc::High` The QR Code can tolerate about 30% erroneous codewords. 307 | 308 | ## Benchmark 309 | 310 | **from_str** in `benches/parse.rs` 311 | ``` 312 | time: [15.734 us 15.758 us 15.782 us] 313 | ``` 314 | 315 | **str_to_brcode** in `benches/to_brcode` 316 | ``` 317 | time: [24.886 us 24.931 us 24.977 us] 318 | ``` 319 | 320 | **edn_from_brcode** in `benches/to_brcode` 321 | ``` 322 | time: [52.670 us 52.795 us 52.929 us] 323 | ``` 324 | 325 | **json_from_brcode** in `benches/to_brcode` 326 | ``` 327 | time: [28.229 us 28.284 us 28.339 us] 328 | ``` 329 | 330 | **both-ways using `BrCode`** 331 | ``` 332 | time: [33.238 us 33.555 us 33.924 us] 333 | ``` 334 | 335 | **both-ways using `Vec<(usize, Data)>`** 336 | ``` 337 | time: [22.867 us 22.958 us 23.107 us] 338 | ``` 339 | 340 | **crc16_ccitt** in `benches/crc16`: 341 | ``` 342 | time: [3.0738 us 3.0825 us 3.0938 us] 343 | ``` 344 | 345 | ## FFI 346 | 347 | ### Clojure FFI 348 | [DOCS](https://github.com/naomijub/brcode/blob/master/clj-brcode/README.md) 349 | 350 | **BR Code as Edn** call function FFI `edn_from_brcode` or use clojar `[clj-brcode "1.1.0-SNAPSHOT"]`. Example: 351 | ```clojure 352 | (ns example.core 353 | (:require [clj-brcode.core :refer :all])) 354 | 355 | (def code "00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304AD38") 356 | 357 | (brcode->edn code) 358 | 359 | ; {:payload-version 1, :initiation-method nil, :merchant-information [{:id 26, :info [{:id 0, :info "BR.GOV.BCB.PIX"}, {:id 1, :info "123e4567-e12b-12d1-a456-426655440000"}]}, {:id 27, :info [{:id 0, :info "BR.COM.OUTRO"}, {:id 1, :info "0123456789"}]}], :merchant-category-code 0, :merchant-name "NOME DO RECEBEDOR", :merchant-city "BRASILIA", :postal-code "70074900", :currency "986", :amount 123.45, :country-code "BR", :field-template [{:reference-label "RP12345678-2019"}], :crc1610 "AD38", :templates [{:id 80, :info [{:id 0, :info "BR.COM.OUTRO"}, {:id 1, :info "0123.ABCD.3456.WXYZ"}]}]} 360 | ``` 361 | 362 | Input: 363 | ```rust 364 | "00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304AD38" 365 | ``` 366 | 367 | Expected Edn: 368 | ```clojure 369 | {:payload-version 1,:initiation-method nil, :merchant-information [ 370 | {:id 26, :info [{ :id 0, :info "BR.GOV.BCB.PIX",}, {:id 1, :info "123e4567-e12b-12d1-a456-426655440000",}]}, 371 | {:id 27, :info [{ :id 0, :info "BR.COM.OUTRO",}, {:id 1, :info "0123456789",}]} 372 | ],:merchant-category-code 0, :merchant-name "NOME DO RECEBEDOR", :merchant-city "BRASILIA", 373 | :postal-code "70074900", :currency "986", :amount 123.45, :country-code "BR", 374 | :field-template [{ :reference-label "RP12345678-2019", }], :crc1610 "AD38", :templates [ 375 | { :id 80, :info [{ :id 0, :info "BR.COM.OUTRO", },{ :id 1, :info "0123.ABCD.3456.WXYZ", }], }] 376 | } 377 | ``` 378 | 379 | 380 | **Edn as BR Code** call function FFI `edn_to_brcode` or use clojar `[clj-brcode "1.1.0-SNAPSHOT"]`. Example: 381 | ```clojure 382 | (ns example.core 383 | (:require [clj-brcode.core :refer :all])) 384 | 385 | (def edn {:payload-version 1, :initiation-method nil, :merchant-information [{:id 26, :info [{:id 0, :info "BR.GOV.BCB.PIX"}, {:id 1, :info "123e4567-e12b-12d1-a456-426655440000"}]}, {:id 27, :info [{:id 0, :info "BR.COM.OUTRO"}, {:id 1, :info "0123456789"}]}], :merchant-category-code 0, :merchant-name "NOME DO RECEBEDOR", :merchant-city "BRASILIA", :postal-code "70074900", :currency "986", :amount 123.45, :country-code "BR", :field-template [{:reference-label "RP12345678-2019"}], :crc1610 "AD38", :templates [{:id 80, :info [{:id 0, :info "BR.COM.OUTRO"}, {:id 1, :info "0123.ABCD.3456.WXYZ"}]}]}) 386 | 387 | (brcode->edn edn) 388 | 389 | ; "00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304AD38" 390 | ``` 391 | 392 | **Other available functions**: 393 | - `json->brcode` 394 | - `brcode->json` 395 | - `crc16-ccitt` 396 | 397 | ### Clojure Benchmark with Criterium 398 | 399 | **brcode->edn** 400 | ``` 401 | Evaluation count : 4644 in 6 samples of 774 calls. 402 | Execution time mean : 131.416626 µs 403 | Execution time std-deviation : 2.218919 µs 404 | Execution time lower quantile : 130.073353 µs ( 2.5%) 405 | Execution time upper quantile : 135.212868 µs (97.5%) 406 | Overhead used : 8.079635 ns 407 | ``` 408 | 409 | **edn->brcode** 410 | ``` 411 | Evaluation count : 3816 in 6 samples of 636 calls. 412 | Execution time mean : 157.407924 µs 413 | Execution time std-deviation : 3.556917 µs 414 | Execution time lower quantile : 154.338082 µs ( 2.5%) 415 | Execution time upper quantile : 162.800564 µs (97.5%) 416 | Overhead used : 8.102766 ns 417 | ``` 418 | 419 | **(-> brcode brcode->edn edn->brcode)** 420 | ``` 421 | Evaluation count : 1920 in 6 samples of 320 calls. 422 | Execution time mean : 344.903181 µs 423 | Execution time std-deviation : 26.518055 µs 424 | Execution time lower quantile : 328.923528 µs ( 2.5%) 425 | Execution time upper quantile : 390.059255 µs (97.5%) 426 | Overhead used : 8.071450 ns 427 | ``` 428 | 429 | ### Node FFI 430 | [neon-brcode](https://github.com/naomijub/neon-brcode) 431 | 432 | ### Dart FFI 433 | [DOCS](https://github.com/naomijub/brcode/blob/master/dartbrcode/README.md) 434 | 435 | **Parse** 436 | ```dart 437 | import 'package:dartbrcode/dartbrcode.dart'; 438 | 439 | final json = '{"payload_version":1,"initiation_method":null,"merchant_account_information":"12345678901234","merchant_information":[{"id":26,"info":[{"id":0,"info":"BR.GOV.BCB.PIX"},{"id":1,"info":"123e4567-e12b-12d1-a456-426655440000"}]},{"id":27,"info":[{"id":0,"info":"BR.COM.OUTRO"},{"id":1,"info":"0123456789"}]}],"merchant_category_code":0,"merchant_name":"NOME DO RECEBEDOR","merchant_city":"BRASILIA","postal_code":"70074900","currency":"986","amount":123.45,"country_code":"BR","field_template":[{"reference_label":"RP12345678-2019"}],"crc1610":"AD38","templates":[{"id":80,"info":[{"id":0,"info":"BR.COM.OUTRO"},{"id":1,"info":"0123.ABCD.3456.WXYZ"}]}]}'; 440 | 441 | void main() { 442 | jsonToBrcode(json); 443 | // '00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304AD38' 444 | } 445 | ``` 446 | 447 | **Emit** 448 | ```dart 449 | import 'package:dartbrcode/dartbrcode.dart'; 450 | 451 | final brcode = '00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304AD38'; 452 | 453 | void main() { 454 | jsonFromBrcode(brcode); 455 | // '{"payload_version":1,"initiation_method":null,"merchant_account_information":"12345678901234","merchant_information":[{"id":26,"info":[{"id":0,"info":"BR.GOV.BCB.PIX"},{"id":1,"info":"123e4567-e12b-12d1-a456-426655440000"}]},{"id":27,"info":[{"id":0,"info":"BR.COM.OUTRO"},{"id":1,"info":"0123456789"}]}],"merchant_category_code":0,"merchant_name":"NOME DO RECEBEDOR","merchant_city":"BRASILIA","postal_code":"70074900","currency":"986","amount":123.45,"country_code":"BR","field_template":[{"reference_label":"RP12345678-2019"}],"crc1610":"AD38","templates":[{"id":80,"info":[{"id":0,"info":"BR.COM.OUTRO"},{"id":1,"info":"0123.ABCD.3456.WXYZ"}]}]}' 456 | } 457 | ``` 458 | 459 | **Other available functions**: 460 | - `crc16Ccitt` 461 | 462 | ### Benchmarks 463 | 464 | * with `dart_benchmark` 465 | **jsonToBrcode** 466 | ``` 467 | For 100 runs: peak: 371 us, bottom: 048 us, avg: ~083 us 468 | ``` 469 | 470 | **brcodeToJson** 471 | ``` 472 | For 100 runs: peak: 327 us, bottom: 069 us, avg: ~101 us 473 | ``` 474 | 475 | * with `benchmark_harness` 476 | **jsonToBrcode** 477 | ``` 478 | For 10 runs: 207.51774227018055 us. 479 | ``` 480 | 481 | **brcodeToJson** 482 | ``` 483 | For 10 runs: 378.68780764861793 us. 484 | ``` 485 | 486 | ## Goals 487 | - [x] Parse BR Code String to `Vec<(usize, Data)>` (more flexible solution); 488 | - [x] Parse BR Code to `BrCode` struct; 489 | - [x] Emit BR Code from `Vec<(usize, Data)>`; 490 | - [x] Emit BR Code from `BrCode` struct; 491 | - [x] CRC16_CCITT 492 | - [x] FFI 493 | -------------------------------------------------------------------------------- /benches/both_ways.rs: -------------------------------------------------------------------------------- 1 | use brcode::{brcode_to_string, from_str, str_to_brcode, to_string}; 2 | use criterion::{criterion_group, criterion_main, Criterion}; 3 | 4 | fn brcode_benchmark(c: &mut Criterion) { 5 | let code = code(); 6 | c.bench_function("using brcode", |b| { 7 | b.iter(|| brcode_to_string(str_to_brcode(&code))) 8 | }); 9 | } 10 | 11 | fn vec_benchmark(c: &mut Criterion) { 12 | let code = code(); 13 | c.bench_function("using vec", |b| b.iter(|| to_string(&from_str(&code)))); 14 | } 15 | 16 | criterion_group!(benches, brcode_benchmark, vec_benchmark); 17 | criterion_main!(benches); 18 | 19 | fn code() -> String { 20 | "00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304AD38" 21 | .to_string() 22 | } 23 | -------------------------------------------------------------------------------- /benches/crc16.rs: -------------------------------------------------------------------------------- 1 | use brcode::crc16_ccitt; 2 | use criterion::{criterion_group, criterion_main, Criterion}; 3 | 4 | fn criterion_benchmark(c: &mut Criterion) { 5 | let message = message(); 6 | c.bench_function("crc16", |b| b.iter(|| crc16_ccitt(&message))); 7 | } 8 | 9 | criterion_group!(benches, criterion_benchmark); 10 | criterion_main!(benches); 11 | 12 | fn message() -> String { 13 | "00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304" 14 | .to_string() 15 | } 16 | -------------------------------------------------------------------------------- /benches/parse.rs: -------------------------------------------------------------------------------- 1 | use brcode::from_str; 2 | use criterion::{criterion_group, criterion_main, Criterion}; 3 | 4 | fn criterion_benchmark(c: &mut Criterion) { 5 | let code = code(); 6 | c.bench_function("parse", |b| b.iter(|| from_str(&code))); 7 | } 8 | 9 | criterion_group!(benches, criterion_benchmark); 10 | criterion_main!(benches); 11 | 12 | fn code() -> String { 13 | "00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304AD38" 14 | .to_string() 15 | } 16 | -------------------------------------------------------------------------------- /benches/to_brcode.rs: -------------------------------------------------------------------------------- 1 | use brcode::{edn_from_brcode, json_from_brcode, str_to_brcode}; 2 | use criterion::{criterion_group, criterion_main, Criterion}; 3 | 4 | fn rust_criterion_benchmark(c: &mut Criterion) { 5 | let code = code(); 6 | c.bench_function("to_brcode", |b| b.iter(|| str_to_brcode(&code))); 7 | } 8 | 9 | fn edn_criterion_benchmark(c: &mut Criterion) { 10 | let code = to_c_char(code()); 11 | c.bench_function("edn_from_brcode", |b| b.iter(|| edn_from_brcode(code))); 12 | } 13 | 14 | fn json_criterion_benchmark(c: &mut Criterion) { 15 | let code = to_c_char(code()); 16 | c.bench_function("json_from_brcode", |b| b.iter(|| json_from_brcode(code))); 17 | } 18 | 19 | criterion_group!( 20 | benches, 21 | rust_criterion_benchmark, 22 | edn_criterion_benchmark, 23 | json_criterion_benchmark 24 | ); 25 | criterion_main!(benches); 26 | 27 | fn code() -> String { 28 | "00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304AD38" 29 | .to_string() 30 | } 31 | 32 | // FFI resources 33 | use std::ffi::CString; 34 | use std::mem; 35 | use std::os::raw::c_char; 36 | 37 | fn to_c_char(s: String) -> *const c_char { 38 | let cs = CString::new(s.as_bytes()).unwrap(); 39 | let ptr = cs.as_ptr(); 40 | mem::forget(cs); 41 | ptr 42 | } 43 | -------------------------------------------------------------------------------- /clj-brcode/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | profiles.clj 5 | pom.xml 6 | pom.xml.asc 7 | *.jar 8 | *.class 9 | /.lein-* 10 | /.nrepl-port 11 | .hgignore 12 | .hg/ 13 | -------------------------------------------------------------------------------- /clj-brcode/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/). 3 | 4 | ## [Unreleased] 5 | ### Changed 6 | - Add a new arity to `make-widget-async` to provide a different widget shape. 7 | 8 | ## [0.1.1] - 2020-09-11 9 | ### Changed 10 | - Documentation on how to make the widgets. 11 | 12 | ### Removed 13 | - `make-widget-sync` - we're all async, all the time. 14 | 15 | ### Fixed 16 | - Fixed widget maker to keep working when daylight savings switches over. 17 | 18 | ## 0.1.0 - 2020-09-11 19 | ### Added 20 | - Files from the new template. 21 | - Widget maker public API - `make-widget-sync`. 22 | 23 | [Unreleased]: https://github.com/your-name/clj-brcode/compare/0.1.1...HEAD 24 | [0.1.1]: https://github.com/your-name/clj-brcode/compare/0.1.0...0.1.1 25 | -------------------------------------------------------------------------------- /clj-brcode/LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. [http://fsf.org/] 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. -------------------------------------------------------------------------------- /clj-brcode/README.md: -------------------------------------------------------------------------------- 1 | # clj-brcode 2 | 3 | Clojure wrapper of `brcode` to parse and emit [PIX BR Code](https://www.bcb.gov.br/content/estabilidadefinanceira/spb_docs/ManualBRCode.pdf). 4 | 5 | ## Usage 6 | 7 | 1. include in your `project.clj` dependencies `[clj-brcode "1.1.0-SNAPSHOT"]`: 8 | 9 | ```clojure 10 | :dependencies [[org.clojure/clojure "1.10.1"] 11 | [clj-brcode "1.1.0-SNAPSHOT"]] 12 | ``` 13 | 2. Copy `libbrcode.*` from [brcode](https://github.com/naomijub/brcode/tree/master/clj-brcode) to your Clojure project root: 14 | - for linux copy `libbrcode.so`. 15 | - for macos copy `libbrcode.dylib`. 16 | - `cargo build --release` project from [git](https://github.com/naomijub/brcode) and copy the `libbrcode.*` from `target/release/libbrcode.*` to your Clojure project root 17 | - Shellscript to get files from release: 18 | 19 | **So** 20 | ```sh 21 | curl -s https://api.github.com/repos/naomijub/brcode/releases/latest \ 22 | | grep "browser_download_url.*so" \ 23 | | cut -d : -f 2,3 \ 24 | | tr -d \" \ 25 | | wget -qi - 26 | ``` 27 | 28 | **dylib** 29 | ```sh 30 | curl -s https://api.github.com/repos/naomijub/brcode/releases/latest \ 31 | | grep "browser_download_url.*dylib" \ 32 | | cut -d : -f 2,3 \ 33 | | tr -d \" \ 34 | | wget -qi - 35 | ``` 36 | 37 | 3. require it in your project namespace `(:require [clj-brcode.core :as pix]))`. 38 | 39 | 4. Use it! 40 | ```clojure 41 | (ns test-code.core 42 | (:require [clj-brcode.core :as pix])) 43 | 44 | (def code "00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304AD38") 45 | 46 | (defn -main 47 | "I don't do a whole lot ... yet." 48 | [& args] 49 | (println (pix/brcode->edn code))) 50 | ``` 51 | 52 | ## Examples 53 | 54 | **brcode->edn** 55 | ```clojure 56 | (def brcode "00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304AD38") 57 | 58 | (def edn {:payload-version 1, :initiation-method nil, :merchant-information [{:id 26, :info [{:id 0, :info "BR.GOV.BCB.PIX"}, {:id 1, :info "123e4567-e12b-12d1-a456-426655440000"}]}, {:id 27, :info [{:id 0, :info "BR.COM.OUTRO"}, {:id 1, :info "0123456789"}]}], :merchant-category-code 0, :merchant-name "NOME DO RECEBEDOR", :merchant-city "BRASILIA", :postal-code "70074900", :currency "986", :amount 123.45, :country-code "BR", :field-template [{:reference-label "RP12345678-2019"}], :crc1610 "AD38", :templates [{:id 80, :info [{:id 0, :info "BR.COM.OUTRO"}, {:id 1, :info "0123.ABCD.3456.WXYZ"}]}]}) 59 | 60 | (= (brcode->edn brcode) edn) 61 | ``` 62 | 63 | **edn->brcode** 64 | ```clojure 65 | (def code "00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304AD38") 66 | (def edn {:payload-version 1, :initiation-method nil, :merchant-account-information "12345678901234" :merchant-information [{:id 26, :info [{:id 0, :info "BR.GOV.BCB.PIX"}, {:id 1, :info "123e4567-e12b-12d1-a456-426655440000"}]}, {:id 27, :info [{:id 0, :info "BR.COM.OUTRO"}, {:id 1, :info "0123456789"}]}], :merchant-category-code 0, :merchant-name "NOME DO RECEBEDOR", :merchant-city "BRASILIA", :postal-code "70074900", :currency "986", :amount 123.45, :country-code "BR", :field-template [{:reference-label "RP12345678-2019"}], :crc1610 "AD38", :templates [{:id 80, :info [{:id 0, :info "BR.COM.OUTRO"}, {:id 1, :info "0123.ABCD.3456.WXYZ"}]}]}) 67 | 68 | (= (edn->brcode edn) code) 69 | 70 | ``` 71 | 72 | **crc16-ccitt** 73 | ```clojure 74 | (def unchecked-code "00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304") 75 | 76 | (= (crc16-ccitt unchecked-code) "AD38") 77 | ``` 78 | 79 | ## Benchmark with Criterium 80 | 81 | **brcode->edn** 82 | ``` 83 | Evaluation count : 4644 in 6 samples of 774 calls. 84 | Execution time mean : 131.416626 µs 85 | Execution time std-deviation : 2.218919 µs 86 | Execution time lower quantile : 130.073353 µs ( 2.5%) 87 | Execution time upper quantile : 135.212868 µs (97.5%) 88 | Overhead used : 8.079635 ns 89 | ``` 90 | 91 | **edn->brcode** 92 | ``` 93 | Evaluation count : 3816 in 6 samples of 636 calls. 94 | Execution time mean : 157.407924 µs 95 | Execution time std-deviation : 3.556917 µs 96 | Execution time lower quantile : 154.338082 µs ( 2.5%) 97 | Execution time upper quantile : 162.800564 µs (97.5%) 98 | Overhead used : 8.102766 ns 99 | ``` 100 | 101 | **(-> brcode brcode->edn edn->brcode)** 102 | ``` 103 | Evaluation count : 1920 in 6 samples of 320 calls. 104 | Execution time mean : 344.903181 µs 105 | Execution time std-deviation : 26.518055 µs 106 | Execution time lower quantile : 328.923528 µs ( 2.5%) 107 | Execution time upper quantile : 390.059255 µs (97.5%) 108 | Overhead used : 8.071450 ns 109 | ``` 110 | 111 | ## Milestones 112 | - [x] parse, `brcode->edn` 113 | - [x] emit `edn->brcode` 114 | 115 | ## License 116 | 117 | GNU LESSER GENERAL PUBLIC LICENSE 118 | Version 3, 29 June 2007 119 | 120 | Copyright (C) 2007 Free Software Foundation, Inc. [http://fsf.org/] 121 | Everyone is permitted to copy and distribute verbatim copies 122 | of this license document, but changing it is not allowed. 123 | 124 | 125 | This version of the GNU Lesser General Public License incorporates 126 | the terms and conditions of version 3 of the GNU General Public 127 | License, supplemented by the additional permissions listed below. 128 | 129 | 0. Additional Definitions. 130 | 131 | As used herein, "this License" refers to version 3 of the GNU Lesser 132 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 133 | General Public License. 134 | 135 | "The Library" refers to a covered work governed by this License, 136 | other than an Application or a Combined Work as defined below. 137 | 138 | An "Application" is any work that makes use of an interface provided 139 | by the Library, but which is not otherwise based on the Library. 140 | Defining a subclass of a class defined by the Library is deemed a mode 141 | of using an interface provided by the Library. 142 | 143 | A "Combined Work" is a work produced by combining or linking an 144 | Application with the Library. The particular version of the Library 145 | with which the Combined Work was made is also called the "Linked 146 | Version". 147 | 148 | The "Minimal Corresponding Source" for a Combined Work means the 149 | Corresponding Source for the Combined Work, excluding any source code 150 | for portions of the Combined Work that, considered in isolation, are 151 | based on the Application, and not on the Linked Version. 152 | 153 | The "Corresponding Application Code" for a Combined Work means the 154 | object code and/or source code for the Application, including any data 155 | and utility programs needed for reproducing the Combined Work from the 156 | Application, but excluding the System Libraries of the Combined Work. 157 | 158 | 1. Exception to Section 3 of the GNU GPL. 159 | 160 | You may convey a covered work under sections 3 and 4 of this License 161 | without being bound by section 3 of the GNU GPL. 162 | 163 | 2. Conveying Modified Versions. 164 | 165 | If you modify a copy of the Library, and, in your modifications, a 166 | facility refers to a function or data to be supplied by an Application 167 | that uses the facility (other than as an argument passed when the 168 | facility is invoked), then you may convey a copy of the modified 169 | version: 170 | 171 | a) under this License, provided that you make a good faith effort to 172 | ensure that, in the event an Application does not supply the 173 | function or data, the facility still operates, and performs 174 | whatever part of its purpose remains meaningful, or 175 | 176 | b) under the GNU GPL, with none of the additional permissions of 177 | this License applicable to that copy. 178 | 179 | 3. Object Code Incorporating Material from Library Header Files. 180 | 181 | The object code form of an Application may incorporate material from 182 | a header file that is part of the Library. You may convey such object 183 | code under terms of your choice, provided that, if the incorporated 184 | material is not limited to numerical parameters, data structure 185 | layouts and accessors, or small macros, inline functions and templates 186 | (ten or fewer lines in length), you do both of the following: 187 | 188 | a) Give prominent notice with each copy of the object code that the 189 | Library is used in it and that the Library and its use are 190 | covered by this License. 191 | 192 | b) Accompany the object code with a copy of the GNU GPL and this license 193 | document. 194 | 195 | 4. Combined Works. 196 | 197 | You may convey a Combined Work under terms of your choice that, 198 | taken together, effectively do not restrict modification of the 199 | portions of the Library contained in the Combined Work and reverse 200 | engineering for debugging such modifications, if you also do each of 201 | the following: 202 | 203 | a) Give prominent notice with each copy of the Combined Work that 204 | the Library is used in it and that the Library and its use are 205 | covered by this License. 206 | 207 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 208 | document. 209 | 210 | c) For a Combined Work that displays copyright notices during 211 | execution, include the copyright notice for the Library among 212 | these notices, as well as a reference directing the user to the 213 | copies of the GNU GPL and this license document. 214 | 215 | d) Do one of the following: 216 | 217 | 0) Convey the Minimal Corresponding Source under the terms of this 218 | License, and the Corresponding Application Code in a form 219 | suitable for, and under terms that permit, the user to 220 | recombine or relink the Application with a modified version of 221 | the Linked Version to produce a modified Combined Work, in the 222 | manner specified by section 6 of the GNU GPL for conveying 223 | Corresponding Source. 224 | 225 | 1) Use a suitable shared library mechanism for linking with the 226 | Library. A suitable mechanism is one that (a) uses at run time 227 | a copy of the Library already present on the user's computer 228 | system, and (b) will operate properly with a modified version 229 | of the Library that is interface-compatible with the Linked 230 | Version. 231 | 232 | e) Provide Installation Information, but only if you would otherwise 233 | be required to provide such information under section 6 of the 234 | GNU GPL, and only to the extent that such information is 235 | necessary to install and execute a modified version of the 236 | Combined Work produced by recombining or relinking the 237 | Application with a modified version of the Linked Version. (If 238 | you use option 4d0, the Installation Information must accompany 239 | the Minimal Corresponding Source and Corresponding Application 240 | Code. If you use option 4d1, you must provide the Installation 241 | Information in the manner specified by section 6 of the GNU GPL 242 | for conveying Corresponding Source.) 243 | 244 | 5. Combined Libraries. 245 | 246 | You may place library facilities that are a work based on the 247 | Library side by side in a single library together with other library 248 | facilities that are not Applications and are not covered by this 249 | License, and convey such a combined library under terms of your 250 | choice, if you do both of the following: 251 | 252 | a) Accompany the combined library with a copy of the same work based 253 | on the Library, uncombined with any other library facilities, 254 | conveyed under the terms of this License. 255 | 256 | b) Give prominent notice with the combined library that part of it 257 | is a work based on the Library, and explaining where to find the 258 | accompanying uncombined form of the same work. 259 | 260 | 6. Revised Versions of the GNU Lesser General Public License. 261 | 262 | The Free Software Foundation may publish revised and/or new versions 263 | of the GNU Lesser General Public License from time to time. Such new 264 | versions will be similar in spirit to the present version, but may 265 | differ in detail to address new problems or concerns. 266 | 267 | Each version is given a distinguishing version number. If the 268 | Library as you received it specifies that a certain numbered version 269 | of the GNU Lesser General Public License "or any later version" 270 | applies to it, you have the option of following the terms and 271 | conditions either of that published version or of any later version 272 | published by the Free Software Foundation. If the Library as you 273 | received it does not specify a version number of the GNU Lesser 274 | General Public License, you may choose any version of the GNU Lesser 275 | General Public License ever published by the Free Software Foundation. 276 | 277 | If the Library as you received it specifies that a proxy can decide 278 | whether future versions of the GNU Lesser General Public License shall 279 | apply, that proxy's public statement of acceptance of any version is 280 | permanent authorization for you to choose that version for the 281 | Library. 282 | -------------------------------------------------------------------------------- /clj-brcode/project.clj: -------------------------------------------------------------------------------- 1 | (defproject clj-brcode "1.1.0-SNAPSHOT" 2 | :description "Clojure wrapper of Rust's `brcode` parser and emitter" 3 | :url "http:https://github.com/naomijub/brcode" 4 | :license {:name "LGPL-3.0" 5 | :url "https://www.eclipse.org/legal/epl-2.0/"} 6 | :dependencies [[org.clojure/clojure "1.10.1"] 7 | [com.github.jnr/jnr-ffi "2.1.16"]] 8 | :main ^:skip-aot clj-brcode.core 9 | :target-path "target/%s" 10 | :profiles {:uberjar {:aot :all 11 | :jvm-opts ["-Dclojure.compiler.direct-linking=true"]}}) 12 | -------------------------------------------------------------------------------- /clj-brcode/src/clj_brcode/core.clj: -------------------------------------------------------------------------------- 1 | (ns clj-brcode.core 2 | (:import jnr.ffi.LibraryLoader) 3 | (:gen-class)) 4 | 5 | (def mem-brcode 6 | (let [lib-brcode (-> (gen-interface :name "LibC" :methods 7 | [[edn_from_brcode [String] String] 8 | [edn_to_brcode [String] String] 9 | [json_from_brcode [String] String] 10 | [json_to_brcode [String] String] 11 | [crc16_ccitt_from_message [String] String]]) 12 | LibraryLoader/create 13 | (.load "brcode"))] 14 | lib-brcode)) 15 | 16 | (defn brcode->edn [s] 17 | (-> mem-brcode (.edn_from_brcode s) read-string)) 18 | 19 | (defn edn->brcode [edn] 20 | (-> mem-brcode (.edn_to_brcode (pr-str edn)))) 21 | 22 | (defn brcode->json [s] 23 | (-> mem-brcode (.json_from_brcode s))) 24 | 25 | (defn json->brcode [json] 26 | (-> mem-brcode (.json_to_brcode json))) 27 | 28 | (defn crc16-ccitt [message] 29 | (-> mem-brcode (.crc16_ccitt_from_message message))) 30 | 31 | -------------------------------------------------------------------------------- /clj-brcode/test/clj_brcode/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-brcode.core-test 2 | (:require [clojure.test :refer :all] 3 | [clj-brcode.core :refer :all])) 4 | 5 | (def code "00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304AD38") 6 | (def edn {:payload-version 1, :initiation-method nil, :merchant-account-information "12345678901234" :merchant-information [{:id 26, :info [{:id 0, :info "BR.GOV.BCB.PIX"}, {:id 1, :info "123e4567-e12b-12d1-a456-426655440000"}]}, {:id 27, :info [{:id 0, :info "BR.COM.OUTRO"}, {:id 1, :info "0123456789"}]}], :merchant-category-code 0, :merchant-name "NOME DO RECEBEDOR", :merchant-city "BRASILIA", :convenience nil, :convenience-fee-fixed nil, :convenience-fee-percentage nil, :postal-code "70074900", :currency "986", :amount 123.45, :country-code "BR", :field-template [{:reference-label "RP12345678-2019"}], :crc1610 "AD38", :templates [{:id 80, :info [{:id 0, :info "BR.COM.OUTRO"}, {:id 1, :info "0123.ABCD.3456.WXYZ"}]}]}) 7 | 8 | (deftest brcode-test 9 | (testing "conform rust result to clojure" 10 | (is (= (brcode->edn code) edn))) 11 | (testing "conform edn to brcode" 12 | (is (= (edn->brcode edn) code)))) 13 | 14 | (def json "{\"payload_version\":1,\"merchant_account_information\":\"12345678901234\",\"merchant_information\":[{\"id\":26,\"info\":[{\"id\":0,\"info\":\"BR.GOV.BCB.PIX\"},{\"id\":1,\"info\":\"123e4567-e12b-12d1-a456-426655440000\"}]},{\"id\":27,\"info\":[{\"id\":0,\"info\":\"BR.COM.OUTRO\"},{\"id\":1,\"info\":\"0123456789\"}]}],\"merchant_category_code\":0,\"merchant_name\":\"NOME DO RECEBEDOR\",\"merchant_city\":\"BRASILIA\",\"postal_code\":\"70074900\",\"currency\":\"986\",\"amount\":123.45,\"country_code\":\"BR\",\"field_template\":[{\"reference_label\":\"RP12345678-2019\"}],\"crc1610\":\"AD38\",\"templates\":[{\"id\":80,\"info\":[{\"id\":0,\"info\":\"BR.COM.OUTRO\"},{\"id\":1,\"info\":\"0123.ABCD.3456.WXYZ\"}]}]}") 15 | (deftest json-brcode-test 16 | (testing "conform rust result to clojure" 17 | (is (= (brcode->json code) json))) 18 | (testing "conform json to brcode" 19 | (is (= (json->brcode json) code)))) 20 | 21 | (def unchecked-code "00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304") 22 | (deftest crc16-test 23 | (testing "biscuit is B8CE" 24 | (is (= (crc16-ccitt "biscuit") "B8CE")) 25 | (is (= (crc16-ccitt unchecked-code) "AD38")))) 26 | 27 | (deftest drop 28 | (testing "access edn after a drop" 29 | (let [e edn 30 | resp (edn->brcode e)] 31 | (is (= resp code)) 32 | (is (= 1 (get e :payload-version))))) 33 | (testing "access code after crc16" 34 | (let [uc unchecked-code 35 | crc (crc16-ccitt uc) 36 | code (str uc crc) 37 | expect (str unchecked-code "AD38")] 38 | (is (= code expect))) 39 | ) 40 | (testing "access code after drop" 41 | (let [c code 42 | edn (brcode->edn c)] 43 | (is (= 1 (get edn :payload-version))) 44 | (is (= (subs c 0 2) "00"))))) -------------------------------------------------------------------------------- /dartbrcode/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.3.0 2 | - `jsonMapToBrcode` - function to encode a Json `Map` as a brcode string. 3 | - `brcodeToJsonMap` - function to parse a brcode string as a Json `Map`. 4 | 5 | ## 0.2.0 6 | - `crc16Ccitt` - returns checksum string of a string message. 7 | 8 | ## 0.1.0 9 | - `jsonToBrcode` - function to encode a Json as a brcode string. 10 | - `brcodeToJson` - function to parse a brcode string as a Json. -------------------------------------------------------------------------------- /dartbrcode/LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. [http://fsf.org/] 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. -------------------------------------------------------------------------------- /dartbrcode/README.md: -------------------------------------------------------------------------------- 1 | # dartbrcode 2 | 3 | Dart wrapper of `brcode` to parse and emit [PIX BR Code](https://www.bcb.gov.br/content/estabilidadefinanceira/spb_docs/ManualBRCode.pdf). 4 | 5 | ## Usage 6 | 7 | 1. Include `dartbrcode` in `pubspec.yaml` 8 | ```yaml 9 | dependencies: 10 | dartbrcode: ^0.3.0 11 | ``` 12 | 2. Copy `libbrcode.*` from [brcode](https://github.com/naomijub/brcode) to your Dart/Flutter project root: 13 | - for Linux/Android copy `libbrcode.so`. 14 | - for macOS/iOS copy `libbrcode.dylib`. 15 | - `cargo build --release` project from [git](https://github.com/naomijub/brcode) and copy the `libbrcode.*` from `target/release/libbrcode.*` to your Dart project root. Mobile in the section `Building for mobile`. 16 | - Shellscript to get files from release: 17 | **So** 18 | ```sh 19 | curl -s https://api.github.com/repos/naomijub/brcode/releases/latest \ 20 | | grep "browser_download_url.*so" \ 21 | | cut -d : -f 2,3 \ 22 | | tr -d \" \ 23 | | wget -qi - 24 | ``` 25 | 26 | **dylib** 27 | ```sh 28 | curl -s https://api.github.com/repos/naomijub/brcode/releases/latest \ 29 | | grep "browser_download_url.*dylib" \ 30 | | cut -d : -f 2,3 \ 31 | | tr -d \" \ 32 | | wget -qi - 33 | ``` 34 | 35 | 3. Use it! 36 | 37 | **Parse** 38 | ```dart 39 | import 'package:dartbrcode/dartbrcode.dart'; 40 | 41 | final json = '{"payload_version":1,"initiation_method":null,"merchant_account_information":"12345678901234","merchant_information":[{"id":26,"info":[{"id":0,"info":"BR.GOV.BCB.PIX"},{"id":1,"info":"123e4567-e12b-12d1-a456-426655440000"}]},{"id":27,"info":[{"id":0,"info":"BR.COM.OUTRO"},{"id":1,"info":"0123456789"}]}],"merchant_category_code":0,"merchant_name":"NOME DO RECEBEDOR","merchant_city":"BRASILIA","postal_code":"70074900","currency":"986","amount":123.45,"country_code":"BR","field_template":[{"reference_label":"RP12345678-2019"}],"crc1610":"AD38","templates":[{"id":80,"info":[{"id":0,"info":"BR.COM.OUTRO"},{"id":1,"info":"0123.ABCD.3456.WXYZ"}]}]}'; 42 | 43 | void main() { 44 | jsonToBrcode(json); 45 | // '00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304AD38' 46 | } 47 | ``` 48 | 49 | **Emit** 50 | ```dart 51 | import 'package:dartbrcode/dartbrcode.dart'; 52 | 53 | final brcode = '00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304AD38'; 54 | 55 | void main() { 56 | jsonFromBrcode(brcode); 57 | // '{"payload_version":1,"initiation_method":null,"merchant_account_information":"12345678901234","merchant_information":[{"id":26,"info":[{"id":0,"info":"BR.GOV.BCB.PIX"},{"id":1,"info":"123e4567-e12b-12d1-a456-426655440000"}]},{"id":27,"info":[{"id":0,"info":"BR.COM.OUTRO"},{"id":1,"info":"0123456789"}]}],"merchant_category_code":0,"merchant_name":"NOME DO RECEBEDOR","merchant_city":"BRASILIA","postal_code":"70074900","currency":"986","amount":123.45,"country_code":"BR","field_template":[{"reference_label":"RP12345678-2019"}],"crc1610":"AD38","templates":[{"id":80,"info":[{"id":0,"info":"BR.COM.OUTRO"},{"id":1,"info":"0123.ABCD.3456.WXYZ"}]}]}' 58 | } 59 | ``` 60 | 61 | **Crc16Ccitt** 62 | ```dart 63 | import 'package:dartbrcode/dartbrcode.dart'; 64 | 65 | final uncheckedBrcode = '00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304'; 66 | 67 | void main() { 68 | crc16Ccitt(uncheckedBrcode); 69 | // 'AD38' 70 | } 71 | ``` 72 | 73 | ## Building for mobile 74 | [Building FFI with Flutter](https://medium.com/flutter-community/using-ffi-on-flutter-plugins-to-run-native-rust-code-d64c0f14f9c2) 75 | 76 | ### Android 77 | 1. Install Android NDK 78 | 2. Add Rust target for android `rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android` 79 | 3. Build cargo for every target 80 | ```sh 81 | # $ANDROID_NDK_HOME is already set and pointing to the Android NDK folder 82 | 83 | # ENV 84 | AARCH64_LINKER=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android26-clang 85 | ARMV7_LINKER=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi26-clang 86 | I686_LINKER=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android26-clang 87 | 88 | # Build 89 | CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=$AARCH64_LINKER cargo build - target aarch64-linux-android - release 90 | CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER=$ARMV7_LINKER cargo build - target armv7-linux-androideabi - release 91 | CARGO_TARGET_I686_LINUX_ANDROID_LINKER=$I686_LINKER cargo build - target i686-linux-android - release 92 | ``` 93 | 4. Files will be found at: 94 | ``` 95 | target/aarch64-linux-android/release/libbrcode.so 96 | target/armv7-linux-androideabi/release/libbrcode.so 97 | target/i686-linux-android/release/libbrcode.so 98 | ``` 99 | 100 | ### iOS 101 | 1. Instal xcode 102 | 2. Add Rust targets for iOS `rustup target add aarch64-apple-ios armv7-apple-ios armv7s-apple-ios x86_64-apple-ios i386-apple-ios` 103 | 3. Install `cargo-lipo` `cargo install cargo-lipo`. 104 | 4. Install `cbindgen` `cargo install cbindgen`. 105 | 5. Build targets for iOS with `cargo lipo --release`. 106 | 6. Create a `cbindgen.toml`: 107 | ```toml 108 | language = "C" 109 | autogen_warning = "// NOTE: Append the lines below to ios/Classes/Brcode.h" 110 | #namespace = "ffi" 111 | #include_guard = "CBINDGEN_BINDINGS_H" 112 | 113 | [defines] 114 | "target_os = ios" = "TARGET_OS_IOS" 115 | "target_os = macos" = "TARGET_OS_MACOS" 116 | ``` 117 | 7. Create C bindigns via: `cbindgen ./src/lib.rs -c cbindgen.toml | grep -v \#include | uniq` 118 | 119 | ## Benchmarks 120 | 121 | ### with `dart_benchmark` 122 | **jsonToBrcode** 123 | ``` 124 | For 100 runs: peak: 371 us, bottom: 048 us, avg: ~083 us 125 | ``` 126 | 127 | **brcodeToJson** 128 | ``` 129 | For 100 runs: peak: 327 us, bottom: 069 us, avg: ~101 us 130 | ``` 131 | 132 | ### with `benchmark_harness` 133 | **jsonToBrcode** 134 | ``` 135 | For 10 runs: 207.51774227018055 us. 136 | ``` 137 | 138 | **brcodeToJson** 139 | ``` 140 | For 10 runs: 378.68780764861793 us. 141 | ``` 142 | 143 | ## Milestones 144 | - [x] parse, `brcodeToJson` 145 | - [x] emit, `jsonToBrcode` 146 | - [x] parse returning `Map` 147 | - [x] emit receiving `Map` as args -------------------------------------------------------------------------------- /dartbrcode/lib/dartbrcode.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:ffi' as ffi; 3 | import 'dart:ffi'; 4 | import 'dart:io'; 5 | import 'package:ffi/ffi.dart'; 6 | 7 | typedef NativeRustUt8ToUt8 = ffi.Pointer Function(ffi.Pointer); 8 | 9 | final osSpecificFile = Platform.isMacOS 10 | ? "libbrcode.dylib" 11 | : Platform.isIOS 12 | ? "libbrcode.a" 13 | : "libbrcode.so"; 14 | final ffi.DynamicLibrary dl = ffi.DynamicLibrary.open(osSpecificFile); 15 | 16 | String jsonToBrcode(String json) { 17 | final json_to_brcode = dl 18 | .lookupFunction("json_to_brcode"); 19 | final Pointer utf8_brcode = Utf8.toUtf8(json).cast(); 20 | final utf8_from_json = json_to_brcode(utf8_brcode); 21 | 22 | return Utf8.fromUtf8(utf8_from_json).toString(); 23 | } 24 | 25 | String jsonMapToBrcode(Map json) { 26 | final jsonStr = jsonEncode(json); 27 | final json_to_brcode = dl 28 | .lookupFunction("json_to_brcode"); 29 | final Pointer utf8_brcode = Utf8.toUtf8(jsonStr).cast(); 30 | final utf8_from_json = json_to_brcode(utf8_brcode); 31 | 32 | return Utf8.fromUtf8(utf8_from_json).toString(); 33 | } 34 | 35 | String brcodeToJson(String code) { 36 | final json_from_brcode = 37 | dl.lookupFunction( 38 | "json_from_brcode"); 39 | final Pointer utf8_brcode = Utf8.toUtf8(code).cast(); 40 | final utf8_to_json = json_from_brcode(utf8_brcode); 41 | 42 | return Utf8.fromUtf8(utf8_to_json).toString(); 43 | } 44 | 45 | Map brcodeToJsonMap(String code) { 46 | final json_from_brcode = 47 | dl.lookupFunction( 48 | "json_from_brcode"); 49 | final Pointer utf8_brcode = Utf8.toUtf8(code).cast(); 50 | final utf8_to_json = json_from_brcode(utf8_brcode); 51 | 52 | return jsonDecode(Utf8.fromUtf8(utf8_to_json).toString()); 53 | } 54 | 55 | String crc16Ccitt(String code) { 56 | final crc16_ccitt_from_message = 57 | dl.lookupFunction( 58 | "crc16_ccitt_from_message"); 59 | final Pointer utf8_message = Utf8.toUtf8(code).cast(); 60 | final checksum = crc16_ccitt_from_message(utf8_message); 61 | 62 | return Utf8.fromUtf8(checksum).toString(); 63 | } 64 | -------------------------------------------------------------------------------- /dartbrcode/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | _fe_analyzer_shared: 5 | dependency: transitive 6 | description: 7 | name: _fe_analyzer_shared 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "7.0.0" 11 | analyzer: 12 | dependency: transitive 13 | description: 14 | name: analyzer 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "0.39.17" 18 | args: 19 | dependency: transitive 20 | description: 21 | name: args 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.6.0" 25 | async: 26 | dependency: transitive 27 | description: 28 | name: async 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "2.4.2" 32 | boolean_selector: 33 | dependency: transitive 34 | description: 35 | name: boolean_selector 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "2.0.0" 39 | charcode: 40 | dependency: transitive 41 | description: 42 | name: charcode 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.1.3" 46 | cli_util: 47 | dependency: transitive 48 | description: 49 | name: cli_util 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "0.2.0" 53 | collection: 54 | dependency: transitive 55 | description: 56 | name: collection 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.14.13" 60 | convert: 61 | dependency: transitive 62 | description: 63 | name: convert 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "2.1.1" 67 | coverage: 68 | dependency: transitive 69 | description: 70 | name: coverage 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "0.14.1" 74 | crypto: 75 | dependency: transitive 76 | description: 77 | name: crypto 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "2.1.5" 81 | csslib: 82 | dependency: transitive 83 | description: 84 | name: csslib 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "0.16.2" 88 | ffi: 89 | dependency: "direct main" 90 | description: 91 | name: ffi 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "0.1.3" 95 | glob: 96 | dependency: transitive 97 | description: 98 | name: glob 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "1.2.0" 102 | html: 103 | dependency: transitive 104 | description: 105 | name: html 106 | url: "https://pub.dartlang.org" 107 | source: hosted 108 | version: "0.14.0+3" 109 | http: 110 | dependency: transitive 111 | description: 112 | name: http 113 | url: "https://pub.dartlang.org" 114 | source: hosted 115 | version: "0.12.2" 116 | http_multi_server: 117 | dependency: transitive 118 | description: 119 | name: http_multi_server 120 | url: "https://pub.dartlang.org" 121 | source: hosted 122 | version: "2.2.0" 123 | http_parser: 124 | dependency: transitive 125 | description: 126 | name: http_parser 127 | url: "https://pub.dartlang.org" 128 | source: hosted 129 | version: "3.1.4" 130 | io: 131 | dependency: transitive 132 | description: 133 | name: io 134 | url: "https://pub.dartlang.org" 135 | source: hosted 136 | version: "0.3.4" 137 | js: 138 | dependency: transitive 139 | description: 140 | name: js 141 | url: "https://pub.dartlang.org" 142 | source: hosted 143 | version: "0.6.2" 144 | logging: 145 | dependency: transitive 146 | description: 147 | name: logging 148 | url: "https://pub.dartlang.org" 149 | source: hosted 150 | version: "0.11.4" 151 | matcher: 152 | dependency: transitive 153 | description: 154 | name: matcher 155 | url: "https://pub.dartlang.org" 156 | source: hosted 157 | version: "0.12.9" 158 | meta: 159 | dependency: transitive 160 | description: 161 | name: meta 162 | url: "https://pub.dartlang.org" 163 | source: hosted 164 | version: "1.2.3" 165 | mime: 166 | dependency: transitive 167 | description: 168 | name: mime 169 | url: "https://pub.dartlang.org" 170 | source: hosted 171 | version: "0.9.7" 172 | node_interop: 173 | dependency: transitive 174 | description: 175 | name: node_interop 176 | url: "https://pub.dartlang.org" 177 | source: hosted 178 | version: "1.1.1" 179 | node_io: 180 | dependency: transitive 181 | description: 182 | name: node_io 183 | url: "https://pub.dartlang.org" 184 | source: hosted 185 | version: "1.1.1" 186 | node_preamble: 187 | dependency: transitive 188 | description: 189 | name: node_preamble 190 | url: "https://pub.dartlang.org" 191 | source: hosted 192 | version: "1.4.12" 193 | package_config: 194 | dependency: transitive 195 | description: 196 | name: package_config 197 | url: "https://pub.dartlang.org" 198 | source: hosted 199 | version: "1.9.3" 200 | path: 201 | dependency: transitive 202 | description: 203 | name: path 204 | url: "https://pub.dartlang.org" 205 | source: hosted 206 | version: "1.7.0" 207 | pedantic: 208 | dependency: transitive 209 | description: 210 | name: pedantic 211 | url: "https://pub.dartlang.org" 212 | source: hosted 213 | version: "1.9.2" 214 | pool: 215 | dependency: transitive 216 | description: 217 | name: pool 218 | url: "https://pub.dartlang.org" 219 | source: hosted 220 | version: "1.4.0" 221 | pub_semver: 222 | dependency: transitive 223 | description: 224 | name: pub_semver 225 | url: "https://pub.dartlang.org" 226 | source: hosted 227 | version: "1.4.4" 228 | shelf: 229 | dependency: transitive 230 | description: 231 | name: shelf 232 | url: "https://pub.dartlang.org" 233 | source: hosted 234 | version: "0.7.9" 235 | shelf_packages_handler: 236 | dependency: transitive 237 | description: 238 | name: shelf_packages_handler 239 | url: "https://pub.dartlang.org" 240 | source: hosted 241 | version: "2.0.0" 242 | shelf_static: 243 | dependency: transitive 244 | description: 245 | name: shelf_static 246 | url: "https://pub.dartlang.org" 247 | source: hosted 248 | version: "0.2.8" 249 | shelf_web_socket: 250 | dependency: transitive 251 | description: 252 | name: shelf_web_socket 253 | url: "https://pub.dartlang.org" 254 | source: hosted 255 | version: "0.2.3" 256 | source_map_stack_trace: 257 | dependency: transitive 258 | description: 259 | name: source_map_stack_trace 260 | url: "https://pub.dartlang.org" 261 | source: hosted 262 | version: "2.0.0" 263 | source_maps: 264 | dependency: transitive 265 | description: 266 | name: source_maps 267 | url: "https://pub.dartlang.org" 268 | source: hosted 269 | version: "0.10.9" 270 | source_span: 271 | dependency: transitive 272 | description: 273 | name: source_span 274 | url: "https://pub.dartlang.org" 275 | source: hosted 276 | version: "1.7.0" 277 | stack_trace: 278 | dependency: transitive 279 | description: 280 | name: stack_trace 281 | url: "https://pub.dartlang.org" 282 | source: hosted 283 | version: "1.9.5" 284 | stream_channel: 285 | dependency: transitive 286 | description: 287 | name: stream_channel 288 | url: "https://pub.dartlang.org" 289 | source: hosted 290 | version: "2.0.0" 291 | string_scanner: 292 | dependency: transitive 293 | description: 294 | name: string_scanner 295 | url: "https://pub.dartlang.org" 296 | source: hosted 297 | version: "1.0.5" 298 | term_glyph: 299 | dependency: transitive 300 | description: 301 | name: term_glyph 302 | url: "https://pub.dartlang.org" 303 | source: hosted 304 | version: "1.1.0" 305 | test: 306 | dependency: "direct dev" 307 | description: 308 | name: test 309 | url: "https://pub.dartlang.org" 310 | source: hosted 311 | version: "1.15.3" 312 | test_api: 313 | dependency: transitive 314 | description: 315 | name: test_api 316 | url: "https://pub.dartlang.org" 317 | source: hosted 318 | version: "0.2.18" 319 | test_core: 320 | dependency: transitive 321 | description: 322 | name: test_core 323 | url: "https://pub.dartlang.org" 324 | source: hosted 325 | version: "0.3.11" 326 | typed_data: 327 | dependency: transitive 328 | description: 329 | name: typed_data 330 | url: "https://pub.dartlang.org" 331 | source: hosted 332 | version: "1.2.0" 333 | vm_service: 334 | dependency: transitive 335 | description: 336 | name: vm_service 337 | url: "https://pub.dartlang.org" 338 | source: hosted 339 | version: "4.2.0" 340 | watcher: 341 | dependency: transitive 342 | description: 343 | name: watcher 344 | url: "https://pub.dartlang.org" 345 | source: hosted 346 | version: "0.9.7+15" 347 | web_socket_channel: 348 | dependency: transitive 349 | description: 350 | name: web_socket_channel 351 | url: "https://pub.dartlang.org" 352 | source: hosted 353 | version: "1.1.0" 354 | webkit_inspection_protocol: 355 | dependency: transitive 356 | description: 357 | name: webkit_inspection_protocol 358 | url: "https://pub.dartlang.org" 359 | source: hosted 360 | version: "0.7.3" 361 | yaml: 362 | dependency: transitive 363 | description: 364 | name: yaml 365 | url: "https://pub.dartlang.org" 366 | source: hosted 367 | version: "2.2.1" 368 | sdks: 369 | dart: ">=2.7.0 <3.0.0" 370 | -------------------------------------------------------------------------------- /dartbrcode/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: dartbrcode 2 | version: 0.3.0 3 | description: Dart wrapper for Rust BrCode 4 | repository: https://github.com/naomijub/brcode 5 | environment: 6 | sdk: '>=2.0.0 <3.0.0' 7 | dependencies: 8 | ffi: ^0.1.3 9 | dev_dependencies: 10 | test: ^1.15.3 -------------------------------------------------------------------------------- /dartbrcode/test/lib.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartbrcode/dartbrcode.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | const json = 5 | "{\"payload_version\":1,\"initiation_method\":null,\"merchant_account_information\":\"12345678901234\",\"merchant_information\":[{\"id\":26,\"info\":[{\"id\":0,\"info\":\"BR.GOV.BCB.PIX\"},{\"id\":1,\"info\":\"123e4567-e12b-12d1-a456-426655440000\"}]},{\"id\":27,\"info\":[{\"id\":0,\"info\":\"BR.COM.OUTRO\"},{\"id\":1,\"info\":\"0123456789\"}]}],\"merchant_category_code\":0,\"merchant_name\":\"NOME DO RECEBEDOR\",\"merchant_city\":\"BRASILIA\",\"postal_code\":\"70074900\",\"currency\":\"986\",\"amount\":123.45,\"country_code\":\"BR\",\"field_template\":[{\"reference_label\":\"RP12345678-2019\"}],\"crc1610\":\"AD38\",\"templates\":[{\"id\":80,\"info\":[{\"id\":0,\"info\":\"BR.COM.OUTRO\"},{\"id\":1,\"info\":\"0123.ABCD.3456.WXYZ\"}]}]}"; 6 | const brcode = 7 | '00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304AD38'; 8 | const uncheckedCode = 9 | '00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304'; 10 | const mapJson = { 11 | 'payload_version': 1, 12 | 'initiation_method': null, 13 | 'merchant_account_information': '12345678901234', 14 | 'merchant_information': [ 15 | { 16 | 'id': 26, 17 | 'info': [ 18 | {'id': 0, 'info': 'BR.GOV.BCB.PIX'}, 19 | {'id': 1, 'info': '123e4567-e12b-12d1-a456-426655440000'} 20 | ] 21 | }, 22 | { 23 | 'id': 27, 24 | 'info': [ 25 | {'id': 0, 'info': 'BR.COM.OUTRO'}, 26 | {'id': 1, 'info': '0123456789'} 27 | ] 28 | } 29 | ], 30 | 'merchant_category_code': 0, 31 | 'merchant_name': 'NOME DO RECEBEDOR', 32 | 'merchant_city': 'BRASILIA', 33 | 'postal_code': '70074900', 34 | 'currency': '986', 35 | 'amount': 123.45, 36 | 'country_code': 'BR', 37 | 'field_template': [ 38 | {'reference_label': 'RP12345678-2019'} 39 | ], 40 | 'crc1610': 'AD38', 41 | 'templates': [ 42 | { 43 | 'id': 80, 44 | 'info': [ 45 | {'id': 0, 'info': 'BR.COM.OUTRO'}, 46 | {'id': 1, 'info': '0123.ABCD.3456.WXYZ'} 47 | ] 48 | } 49 | ] 50 | }; 51 | void main() { 52 | test('generated brcode is correct for json', () { 53 | final actual = jsonToBrcode(json); 54 | expect(actual, equals(brcode)); 55 | }); 56 | 57 | test('generated json is correct for brcode', () { 58 | final actual = brcodeToJson(brcode); 59 | expect(actual, equals(json)); 60 | }); 61 | 62 | test('biscuit checksum', () { 63 | final actual = crc16Ccitt('biscuit'); 64 | expect(actual, equals('B8CE')); 65 | }); 66 | 67 | test('unchecked code checksum', () { 68 | final actual = crc16Ccitt(uncheckedCode); 69 | expect(actual, equals('AD38')); 70 | }); 71 | 72 | test('generated json Map is correct for brcode', () { 73 | final actual = brcodeToJsonMap(brcode); 74 | expect(actual, equals(mapJson)); 75 | }); 76 | 77 | test('generated brcode is correct for json map', () { 78 | final actual = jsonMapToBrcode(mapJson); 79 | expect(actual, equals(brcode)); 80 | }); 81 | } 82 | -------------------------------------------------------------------------------- /jvm-brcode/.gitattributes: -------------------------------------------------------------------------------- 1 | # 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | # 4 | # These are explicitly windows files and should use crlf 5 | *.bat text eol=crlf 6 | 7 | -------------------------------------------------------------------------------- /jvm-brcode/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore Gradle project-specific cache directory 2 | .gradle 3 | 4 | # Ignore Gradle build output directory 5 | build 6 | -------------------------------------------------------------------------------- /jvm-brcode/Makefile: -------------------------------------------------------------------------------- 1 | build-jar: 2 | rm jvm-brcode-*.jar 3 | rm -rf build/libs/*.jar 4 | gradle build 5 | cp build/libs/jvm-brcode-*.jar ./ -------------------------------------------------------------------------------- /jvm-brcode/README.md: -------------------------------------------------------------------------------- 1 | # Jvm-brcode 2 | 3 | **Not released to maven yet** if you wish to use it download it from source. -------------------------------------------------------------------------------- /jvm-brcode/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'maven-publish' 4 | } 5 | 6 | repositories { 7 | jcenter() 8 | } 9 | 10 | publishing { 11 | publications { 12 | brcodeLib(MavenPublication) { 13 | from components.java 14 | pom { 15 | name = "jvm-brcode" 16 | description = "Java wrapper for PIX BRCODE" 17 | url = "https://github.com/naomijub/brcode" 18 | licenses { 19 | license { 20 | name = "GNU Lesser General Public License v3.0" 21 | url = "https://github.com/naomijub/brcode/blob/master/LICENSE" 22 | } 23 | } 24 | developers { 25 | developer { 26 | id = "naomijub" 27 | name = "Julia Naomi" 28 | email = "jnboeira@outlook.com" 29 | } 30 | } 31 | } 32 | } 33 | } 34 | } 35 | 36 | version = '0.1.1' 37 | dependencies { 38 | 39 | implementation 'com.github.jnr:jnr-ffi:2.1.10' 40 | 41 | testImplementation 'junit:junit:4.13' 42 | } 43 | -------------------------------------------------------------------------------- /jvm-brcode/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/brcode/71a0857912b8add2ab0a197eb8b7566a8b3ad3c3/jvm-brcode/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /jvm-brcode/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /jvm-brcode/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /jvm-brcode/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto init 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto init 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :init 68 | @rem Get command-line arguments, handling Windows variants 69 | 70 | if not "%OS%" == "Windows_NT" goto win9xME_args 71 | 72 | :win9xME_args 73 | @rem Slurp the command line arguments. 74 | set CMD_LINE_ARGS= 75 | set _SKIP=2 76 | 77 | :win9xME_args_slurp 78 | if "x%~1" == "x" goto execute 79 | 80 | set CMD_LINE_ARGS=%* 81 | 82 | :execute 83 | @rem Setup the command line 84 | 85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 86 | 87 | 88 | @rem Execute Gradle 89 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 90 | 91 | :end 92 | @rem End local scope for the variables with windows NT shell 93 | if "%ERRORLEVEL%"=="0" goto mainEnd 94 | 95 | :fail 96 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 97 | rem the _cmd.exe /c_ return code! 98 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 99 | exit /b 1 100 | 101 | :mainEnd 102 | if "%OS%"=="Windows_NT" endlocal 103 | 104 | :omega 105 | -------------------------------------------------------------------------------- /jvm-brcode/settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * The settings file is used to specify which projects to include in your build. 5 | * 6 | * Detailed information about configuring a multi-project build in Gradle can be found 7 | * in the user manual at https://docs.gradle.org/6.5.1/userguide/multi_project_builds.html 8 | */ 9 | 10 | rootProject.name = 'jvm-brcode' 11 | -------------------------------------------------------------------------------- /jvm-brcode/src/main/java/jvm/brcode/Brcode.java: -------------------------------------------------------------------------------- 1 | package jvm.brcode; 2 | 3 | import jnr.ffi.LibraryLoader; 4 | 5 | public class Brcode { 6 | private LibC libc; 7 | public Brcode() { 8 | libc = LibraryLoader.create(LibC.class).load("brcode"); 9 | } 10 | 11 | public interface LibC { 12 | String json_from_brcode(String s); 13 | String json_to_brcode(String s); 14 | String crc16_ccitt_from_message(String s); 15 | } 16 | 17 | public boolean someBrcodeMethod() { 18 | return true; 19 | } 20 | 21 | public String brcodeToJson(String brcode) { 22 | return libc.json_from_brcode(brcode); 23 | } 24 | 25 | public String jsonToBrcode(String json) { 26 | return libc.json_to_brcode(json); 27 | } 28 | 29 | public String crc16CCITTFromMessage(String message) { 30 | return libc.crc16_ccitt_from_message(message); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /jvm-brcode/src/test/java/jvm/brcode/BrcodeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Java source file was generated by the Gradle 'init' task. 3 | */ 4 | package jvm.brcode; 5 | 6 | import org.junit.Test; 7 | import static org.junit.Assert.*; 8 | 9 | public class BrcodeTest { 10 | String code = "00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304AD38"; 11 | String json = "{\"payload_version\":1,\"merchant_account_information\":\"12345678901234\",\"merchant_information\":[{\"id\":26,\"info\":[{\"id\":0,\"info\":\"BR.GOV.BCB.PIX\"},{\"id\":1,\"info\":\"123e4567-e12b-12d1-a456-426655440000\"}]},{\"id\":27,\"info\":[{\"id\":0,\"info\":\"BR.COM.OUTRO\"},{\"id\":1,\"info\":\"0123456789\"}]}],\"merchant_category_code\":0,\"merchant_name\":\"NOME DO RECEBEDOR\",\"merchant_city\":\"BRASILIA\",\"postal_code\":\"70074900\",\"currency\":\"986\",\"amount\":123.45,\"country_code\":\"BR\",\"field_template\":[{\"reference_label\":\"RP12345678-2019\"}],\"crc1610\":\"AD38\",\"templates\":[{\"id\":80,\"info\":[{\"id\":0,\"info\":\"BR.COM.OUTRO\"},{\"id\":1,\"info\":\"0123.ABCD.3456.WXYZ\"}]}]}"; 12 | String uncheckedCode = "00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304"; 13 | 14 | @Test public void testSomeBrcodeMethod() { 15 | Brcode classUnderTest = new Brcode(); 16 | assertTrue("someBrcodeMethod should return 'true'", classUnderTest.someBrcodeMethod()); 17 | } 18 | 19 | @Test public void testbrcodeToJson() { 20 | Brcode brcode = new Brcode(); 21 | String jsonResult = brcode.brcodeToJson(code); 22 | assertEquals(jsonResult, json); 23 | } 24 | 25 | @Test public void testJsonToBrcode() { 26 | Brcode brcode = new Brcode(); 27 | String brcodeResult = brcode.jsonToBrcode(json); 28 | assertEquals(brcodeResult, code); 29 | } 30 | 31 | @Test public void testCrc16() { 32 | Brcode brcode = new Brcode(); 33 | String crc16 = brcode.crc16CCITTFromMessage(uncheckedCode); 34 | assertEquals(crc16, "AD38"); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /libbrcode.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/brcode/71a0857912b8add2ab0a197eb8b7566a8b3ad3c3/libbrcode.dylib -------------------------------------------------------------------------------- /libbrcode.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/brcode/71a0857912b8add2ab0a197eb8b7566a8b3ad3c3/libbrcode.so -------------------------------------------------------------------------------- /src/aux.rs: -------------------------------------------------------------------------------- 1 | use crate::parse; 2 | use std::{collections::HashMap, iter::FromIterator}; 3 | 4 | #[derive(Debug, PartialEq, Clone)] 5 | pub(crate) struct HashBrCode(pub HashMap); 6 | 7 | impl HashBrCode { 8 | pub fn new(code: Vec<(usize, parse::Data)>) -> Self { 9 | code.into_iter().collect() 10 | } 11 | } 12 | 13 | #[derive(Debug, PartialEq, Clone)] 14 | pub(crate) enum Data { 15 | Str(String), 16 | Hash(HashBrCode), 17 | } 18 | 19 | impl Data { 20 | pub fn to_str(&self) -> String { 21 | match self { 22 | Data::Str(s) => String::from(s), 23 | _ => String::new(), 24 | } 25 | } 26 | 27 | pub fn to_hash(&self) -> HashMap { 28 | match self { 29 | Data::Hash(map) => (map.0).to_owned(), 30 | _ => HashMap::new(), 31 | } 32 | } 33 | } 34 | 35 | impl FromIterator<(usize, parse::Data)> for HashBrCode { 36 | fn from_iter(tuples: I) -> Self 37 | where 38 | I: IntoIterator, 39 | { 40 | let mut m = HashMap::new(); 41 | for (k, v) in tuples { 42 | m.entry(k).or_insert(match v { 43 | parse::Data::Single(s) => Data::Str(s), 44 | parse::Data::Vector(v) => { 45 | let map = v.into_iter().collect(); 46 | Data::Hash(map) 47 | } 48 | }); 49 | } 50 | Self(m) 51 | } 52 | } 53 | 54 | pub fn crc16_ccitt(message: &str) -> String { 55 | let mut crc: u16 = 0xFFFF; // initial value 56 | let polynomial: u16 = 0x1021; // 0001 0000 0010 0001 (0, 5, 12) 57 | let bytes = message.as_bytes(); 58 | 59 | for b in bytes { 60 | for i in 0u16..8u16 { 61 | let bit = (b >> (7 - i) & 1) == 1; 62 | let c15 = (crc >> 15 & 1) == 1; 63 | crc <<= 1; 64 | if c15 ^ bit { 65 | crc ^= polynomial 66 | }; 67 | } 68 | } 69 | 70 | crc &= 0xffff; 71 | 72 | format!("{:X}", crc).prepend_remaining_length(4, '0') 73 | } 74 | 75 | trait Field { 76 | fn prepend_remaining_length(&self, length: usize, character: char) -> String; 77 | } 78 | 79 | impl Field for String { 80 | fn prepend_remaining_length(&self, length: usize, character: char) -> String { 81 | let mut string = self.to_owned(); 82 | let limit = length - string.len(); 83 | 84 | for _i in 0..limit { 85 | string.insert(0, character); 86 | } 87 | 88 | string 89 | } 90 | } 91 | 92 | #[cfg(test)] 93 | mod test { 94 | use super::*; 95 | use crate::parse as p; 96 | 97 | #[test] 98 | fn creates_simple_map() { 99 | let vec = vec![ 100 | (0usize, p::Data::Single("01".to_string())), 101 | (4usize, p::Data::Single("12345678901234".to_string())), 102 | ]; 103 | let mut expected = HashMap::new(); 104 | expected.insert(0usize, Data::Str("01".to_string())); 105 | expected.insert(4usize, Data::Str("12345678901234".to_string())); 106 | 107 | let hash: HashBrCode = vec.into_iter().collect(); 108 | 109 | assert_eq!(hash.0, expected); 110 | } 111 | 112 | #[test] 113 | fn test_crc16_ccitt() { 114 | let crc16 = crc16_ccitt("123456789"); 115 | let expected = "29B1"; 116 | 117 | assert_eq!(crc16, expected); 118 | } 119 | 120 | #[test] 121 | fn creates_nested_map() { 122 | let vec = vec![ 123 | (0usize, p::Data::Single("01".to_string())), 124 | (4usize, p::Data::Single("12345678901234".to_string())), 125 | ( 126 | 26, 127 | p::Data::Vector(vec![ 128 | (0, p::Data::Single("BR.GOV.BCB.PIX".to_string())), 129 | ( 130 | 1, 131 | p::Data::Single("123e4567-e12b-12d1-a456-426655440000".to_string()), 132 | ), 133 | ]), 134 | ), 135 | ( 136 | 27, 137 | p::Data::Vector(vec![ 138 | (0, p::Data::Single("BR.COM.OUTRO".to_string())), 139 | (1, p::Data::Single("0123456789".to_string())), 140 | ]), 141 | ), 142 | (52, p::Data::Single("0000".to_string())), 143 | ]; 144 | let mut expected = HashMap::new(); 145 | expected.insert(0usize, Data::Str("01".to_string())); 146 | expected.insert(4usize, Data::Str("12345678901234".to_string())); 147 | expected.insert(52usize, Data::Str("0000".to_string())); 148 | expected.insert( 149 | 26usize, 150 | Data::Hash({ 151 | let mut m_26 = HashMap::new(); 152 | m_26.insert(0usize, Data::Str("BR.GOV.BCB.PIX".to_string())); 153 | m_26.insert( 154 | 1usize, 155 | Data::Str("123e4567-e12b-12d1-a456-426655440000".to_string()), 156 | ); 157 | HashBrCode(m_26) 158 | }), 159 | ); 160 | expected.insert( 161 | 27usize, 162 | Data::Hash({ 163 | let mut m_27 = HashMap::new(); 164 | m_27.insert(0usize, Data::Str("BR.COM.OUTRO".to_string())); 165 | m_27.insert(1usize, Data::Str("0123456789".to_string())); 166 | HashBrCode(m_27) 167 | }), 168 | ); 169 | 170 | let hash: HashBrCode = vec.into_iter().collect(); 171 | 172 | assert_eq!(hash.0, expected); 173 | } 174 | 175 | #[test] 176 | fn prepends_the_remaining_length() { 177 | let missing_characters = "123".to_string(); 178 | let with_all_characters = "12345".to_string(); 179 | 180 | assert_eq!(missing_characters.prepend_remaining_length(5, '0'), "00123"); 181 | assert_eq!( 182 | with_all_characters.prepend_remaining_length(5, '0'), 183 | "12345" 184 | ); 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/emit.rs: -------------------------------------------------------------------------------- 1 | use crate::parse::Data; 2 | 3 | pub(crate) fn emit(code: &[(usize, Data)]) -> String { 4 | code.iter() 5 | .map(|e| match &e.1 { 6 | Data::Single(s) => format!("{:02}{:02}{}", e.0, s.len(), s), 7 | Data::Vector(v) => { 8 | let inner = v 9 | .iter() 10 | .map(|ve| match &ve.1 { 11 | Data::Single(vs) => (vs.len(), format!("{:02}{:02}{}", ve.0, vs.len(), vs)), 12 | _ => (0, String::new()), 13 | }) 14 | .fold((0usize, String::new()), |acc, e| { 15 | (acc.0 + e.0 + 4, acc.1 + &e.1) 16 | }); 17 | format!("{:02}{:02}{}", e.0, inner.0, inner.1) 18 | } 19 | }) 20 | .collect::() 21 | } 22 | 23 | #[cfg(test)] 24 | mod test { 25 | use super::emit; 26 | use crate::from_str; 27 | 28 | #[test] 29 | fn emits_same_data_as_receives() { 30 | let code = "00020104141234567890123426580014BR.GOV.BCB.PIX0136123e4567-e12b-12d1-a456-42665544000027300012BR.COM.OUTRO011001234567895204000053039865406123.455802BR5917NOME DO RECEBEDOR6008BRASILIA61087007490062190515RP12345678-201980390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304AD38"; 31 | let parsed_data = from_str(code); 32 | let emited = emit(&parsed_data); 33 | 34 | assert_eq!(code, emited); 35 | } 36 | 37 | #[test] 38 | fn helloworld_in_tag_00() { 39 | let code = "26062602oi"; 40 | let parsed_data = from_str(code); 41 | let emited = emit(&parsed_data); 42 | 43 | assert_eq!(code, emited); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod aux; 2 | pub(crate) mod emit; 3 | pub(crate) mod model; 4 | pub(crate) mod parse; 5 | 6 | pub use aux::crc16_ccitt; 7 | pub use model::{BrCode, Info, Label, MerchantInfo, Template}; 8 | pub use parse::Data; 9 | pub use qrcode_generator::QrCodeEcc; 10 | 11 | pub fn from_str(code: &str) -> Vec<(usize, parse::Data)> { 12 | parse::parse(code, 99) 13 | } 14 | 15 | pub fn to_string(code: &[(usize, parse::Data)]) -> String { 16 | emit::emit(&code) 17 | } 18 | 19 | pub fn brcode_to_string(code: BrCode) -> String { 20 | code.encode() 21 | } 22 | 23 | pub fn str_to_brcode(code: &str) -> BrCode { 24 | let codes = parse::parse(code, 99); 25 | BrCode::from(codes) 26 | } 27 | 28 | // FFI 29 | use std::ffi::{CStr, CString}; 30 | use std::mem; 31 | use std::os::raw::c_char; 32 | use std::str; 33 | 34 | fn chars_to_string(pointer: *const c_char) -> String { 35 | let slice = unsafe { CStr::from_ptr(pointer).to_bytes() }; 36 | drop(pointer); 37 | str::from_utf8(slice).unwrap().to_string() 38 | } 39 | 40 | fn to_c_char(s: &str) -> *const c_char { 41 | let cs = CString::new(s.as_bytes()).unwrap(); 42 | let ptr = cs.as_ptr(); 43 | mem::forget(cs); 44 | drop(s); 45 | ptr 46 | } 47 | 48 | #[no_mangle] 49 | pub extern "C" fn crc16_ccitt_from_message(message: *const c_char) -> *const c_char { 50 | let message_str = chars_to_string(message); 51 | let checksum = crc16_ccitt(&message_str); 52 | drop(message_str); 53 | to_c_char(&checksum) 54 | } 55 | 56 | // Edn 57 | #[no_mangle] 58 | pub extern "C" fn edn_from_brcode(edn: *const c_char) -> *const c_char { 59 | let edn_str = chars_to_string(edn); 60 | let brcode = str_to_brcode(&edn_str); 61 | drop(edn); 62 | to_c_char(&edn_rs::to_string(brcode)) 63 | } 64 | 65 | #[no_mangle] 66 | pub extern "C" fn edn_to_brcode(edn: *const c_char) -> *const c_char { 67 | let edn_str = chars_to_string(edn); 68 | let brcode: BrCode = edn_rs::from_str(&edn_str).unwrap(); 69 | drop(edn); 70 | to_c_char(&brcode_to_string(brcode)) 71 | } 72 | 73 | #[no_mangle] 74 | pub extern "C" fn edn_to_svg_brcode(edn: *const c_char) -> *const c_char { 75 | let edn_str = chars_to_string(edn); 76 | let brcode: BrCode = edn_rs::from_str(&edn_str).unwrap(); 77 | let svg = brcode.to_svg_standard_string(); 78 | drop(edn); 79 | to_c_char(&svg) 80 | } 81 | 82 | #[no_mangle] 83 | pub extern "C" fn edn_to_svg_file(edn: *const c_char, file_path: *const c_char) { 84 | let edn_str = chars_to_string(edn); 85 | let file_path_str = chars_to_string(file_path); 86 | let brcode: BrCode = edn_rs::from_str(&edn_str).unwrap(); 87 | drop(edn); drop(file_path); 88 | brcode.to_standard_svg_file(&file_path_str); 89 | } 90 | 91 | // Json 92 | #[no_mangle] 93 | pub extern "C" fn json_from_brcode(json: *const c_char) -> *const c_char { 94 | let json_str = chars_to_string(json); 95 | let brcode = str_to_brcode(&json_str); 96 | drop(json); 97 | to_c_char(&serde_json::to_string(&brcode).unwrap_or_else(|_| "error".to_string())) 98 | } 99 | 100 | #[no_mangle] 101 | pub extern "C" fn json_to_brcode(json: *const c_char) -> *const c_char { 102 | let json_str = chars_to_string(json); 103 | let brcode: BrCode = serde_json::from_str(&json_str).unwrap(); 104 | drop(json); 105 | to_c_char(&brcode_to_string(brcode)) 106 | } 107 | 108 | #[no_mangle] 109 | pub extern "C" fn json_to_svg_brcode(json: *const c_char) -> *const c_char { 110 | let json_str = chars_to_string(json); 111 | let brcode: BrCode = serde_json::from_str(&json_str).unwrap(); 112 | let svg = brcode.to_svg_standard_string(); 113 | drop(json); 114 | to_c_char(&svg) 115 | } 116 | 117 | #[no_mangle] 118 | pub extern "C" fn json_to_svg_file(json: *const c_char, file_path: *const c_char) { 119 | let json_str = chars_to_string(json); 120 | let file_path_str = chars_to_string(file_path); 121 | let brcode: BrCode = serde_json::from_str(&json_str).unwrap(); 122 | drop(json); drop(file_path); 123 | brcode.to_standard_svg_file(&file_path_str); 124 | } 125 | -------------------------------------------------------------------------------- /src/model.rs: -------------------------------------------------------------------------------- 1 | use crate::aux::HashBrCode; 2 | use crate::parse::Data; 3 | use edn_derive::{Deserialize, Serialize}; 4 | use qrcode_generator::QrCodeEcc; 5 | use serde_derive::{Deserialize as SerdeDeserialize, Serialize as SerdeSerialize}; 6 | 7 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, SerdeDeserialize, SerdeSerialize)] 8 | pub struct BrCode { 9 | pub payload_version: u8, 10 | #[serde(skip_serializing_if = "Option::is_none")] 11 | pub initiation_method: Option, 12 | #[serde(skip_serializing_if = "Option::is_none")] 13 | pub merchant_account_information: Option, 14 | pub merchant_information: Vec, 15 | pub merchant_category_code: u32, 16 | pub merchant_name: String, 17 | pub merchant_city: String, 18 | #[serde(skip_serializing_if = "Option::is_none")] 19 | pub convenience: Option, //{pub type 55 pub kind pub scalar} 20 | #[serde(skip_serializing_if = "Option::is_none")] 21 | pub convenience_fee_fixed: Option, 22 | #[serde(skip_serializing_if = "Option::is_none")] // {pub type 56 pub kind pub scalar} 23 | pub convenience_fee_percentage: Option, 24 | #[serde(skip_serializing_if = "Option::is_none")] // {pub type 57 pub kind pub scalar} 25 | pub postal_code: Option, 26 | pub currency: String, 27 | #[serde(skip_serializing_if = "Option::is_none")] 28 | pub amount: Option, 29 | pub country_code: String, 30 | pub field_template: Vec