iso6709parse-0.1.2/.cargo_vcs_info.json0000644000000001360000000000100132070ustar { "git": { "sha1": "d5325e93c6e46a4013fa876f05ba5af240debb55" }, "path_in_vcs": "" }iso6709parse-0.1.2/.devcontainer/devcontainer.json000064400000000000000000000021371046102023000201150ustar 00000000000000// For format details, see https://aka.ms/devcontainer.json. For config options, see the // README at: https://github.com/devcontainers/templates/tree/main/src/rust { "name": "Rust", // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile "image": "mcr.microsoft.com/devcontainers/rust:1-1-bullseye", // Use 'mounts' to make the cargo cache persistent in a Docker Volume. // "mounts": [ // { // "source": "devcontainer-cargo-cache-${devcontainerId}", // "target": "/usr/local/cargo", // "type": "volume" // } // ] // Features to add to the dev container. More info: https://containers.dev/features. // "features": {}, // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], // Use 'postCreateCommand' to run commands after the container is created. "postCreateCommand": "chmod -R vscode:rust-lang ~/.cargo", // Configure tool-specific properties. // "customizations": {}, // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. "remoteUser": "vscode" } iso6709parse-0.1.2/.github/workflows/rust-ci.yml000064400000000000000000000010511046102023000175020ustar 00000000000000name: Rust CI on: push: branches: - main pull_request: branches: - main jobs: build: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v2 - name: Set up Rust uses: actions-rs/toolchain@v1 with: toolchain: stable override: true override-file: Cargo.toml - name: Build run: cargo build --verbose - name: Test run: cargo test --verbose - name: Lint run: cargo clippy -- -D warningsiso6709parse-0.1.2/.gitignore000064400000000000000000000000241046102023000137630ustar 00000000000000/target /Cargo.lock iso6709parse-0.1.2/Cargo.lock0000644000000467500000000000100111760ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "aho-corasick" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b8f9420f797f2d9e935edf629310eb938a0d839f984e25327f3c7eed22300c" dependencies = [ "memchr", ] [[package]] name = "anes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstyle" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" [[package]] name = "approx" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" dependencies = [ "num-traits", ] [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] name = "bumpalo" version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01" dependencies = [ "libc", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "ciborium" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" dependencies = [ "ciborium-io", "ciborium-ll", "serde", ] [[package]] name = "ciborium-io" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" [[package]] name = "ciborium-ll" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" dependencies = [ "ciborium-io", "half", ] [[package]] name = "clap" version = "4.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" version = "4.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa" dependencies = [ "anstyle", "clap_lex", ] [[package]] name = "clap_lex" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" [[package]] name = "criterion" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" dependencies = [ "anes", "cast", "ciborium", "clap", "criterion-plot", "is-terminal", "itertools", "num-traits", "once_cell", "oorandom", "plotters", "rayon", "regex", "serde", "serde_derive", "serde_json", "tinytemplate", "walkdir", ] [[package]] name = "criterion-plot" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", "itertools", ] [[package]] name = "crossbeam-channel" version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-deque" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", "memoffset", "scopeguard", ] [[package]] name = "crossbeam-utils" version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] [[package]] name = "either" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "errno" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" dependencies = [ "errno-dragonfly", "libc", "windows-sys", ] [[package]] name = "errno-dragonfly" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" dependencies = [ "cc", "libc", ] [[package]] name = "geo-types" version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9705398c5c7b26132e74513f4ee7c1d7dafd786004991b375c172be2be0eecaa" dependencies = [ "approx", "num-traits", "serde", ] [[package]] name = "half" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "hermit-abi" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "is-terminal" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", "rustix", "windows-sys", ] [[package]] name = "iso6709parse" version = "0.1.2" dependencies = [ "criterion", "geo-types", "latlon", "nom", ] [[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] [[package]] name = "latlon" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d42b0e12043f68bfbe58fa335878a1504eb7ee741099e28f0c4eee28c515fbf0" dependencies = [ "geo-types", "lazy_static", "regex", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libm" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" [[package]] name = "linux-raw-sys" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" [[package]] name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg", ] [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "nom" version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", ] [[package]] name = "num-traits" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", "libm", ] [[package]] name = "num_cpus" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi", "libc", ] [[package]] name = "once_cell" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "oorandom" version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "plotters" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" dependencies = [ "num-traits", "plotters-backend", "plotters-svg", "wasm-bindgen", "web-sys", ] [[package]] name = "plotters-backend" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" [[package]] name = "plotters-svg" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" dependencies = [ "plotters-backend", ] [[package]] name = "proc-macro2" version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] [[package]] name = "rayon" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" dependencies = [ "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" dependencies = [ "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", "num_cpus", ] [[package]] name = "regex" version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "rustix" version = "0.38.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys", ] [[package]] name = "ryu" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" version = "1.0.181" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d3e73c93c3240c0bda063c239298e633114c69a888c3e37ca8bb33f343e9890" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.181" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be02f6cb0cd3a5ec20bbcfbcbd749f57daddb1a0882dc2e46a6c236c90b977ed" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "syn" version = "2.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tinytemplate" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ "serde", "serde_json", ] [[package]] name = "unicode-ident" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "walkdir" version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "wasm-bindgen" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" iso6709parse-0.1.2/Cargo.toml0000644000000023730000000000100112120ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "iso6709parse" version = "0.1.2" authors = ["Tim Reed "] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Parses coordinates in ISO6709 format from strings" readme = "README.md" keywords = [ "iso6709", "latlon", "coordinates", "parse", "gps", ] categories = ["parser-implementations"] license = "MIT" repository = "https://github.com/TimLikesTacos/iso6709parse.git" [lib] name = "iso6709parse" path = "src/lib.rs" [[bench]] name = "benchmark" path = "benches/benchmark.rs" harness = false [dependencies.geo-types] version = "0.7" [dependencies.nom] version = "7" [dev-dependencies.criterion] version = "0.5" [dev-dependencies.latlon] version = "0.1.3" iso6709parse-0.1.2/Cargo.toml.orig000064400000000000000000000010241046102023000146630ustar 00000000000000[package] name = "iso6709parse" authors = ["Tim Reed "] version = "0.1.2" edition = "2021" license = "MIT" categories = ["parser-implementations"] keywords = ["iso6709", "latlon", "coordinates", "parse", "gps"] repository = "https://github.com/TimLikesTacos/iso6709parse.git" readme = "README.md" description = "Parses coordinates in ISO6709 format from strings" [dependencies] nom = "7" geo-types = "0.7" [dev-dependencies] criterion = "0.5" latlon = "0.1.3" [[bench]] name = "benchmark" harness = false iso6709parse-0.1.2/LICENSE000064400000000000000000000020501046102023000130010ustar 00000000000000MIT License Copyright (c) 2023 Tim Reed Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.iso6709parse-0.1.2/README.md000064400000000000000000000017641046102023000132660ustar 00000000000000# ISO6709 parser [![Rust CI](https://github.com/TimLikesTacos/iso6709parse/actions/workflows/rust-ci.yml/badge.svg)](https://github.com/TimLikesTacos/iso6709parse/actions/workflows/rust-ci.yml) This library uses the `nom` crate to create parsers to quickly convert ISO6709 formatted strings. This results in a much faster parse than using Regex based libraries, from 4 to 10x faster. `From` traits have been implemented for the `geo_types` crate for easy conversion from strings. Supports formats for latitude with `N` or `S` and `E` and `W` instead of `+` or `-`: `±DD.DD` `±DDMM.MMM` `±DDMMSS.SSS` for longitude: `±DDD.DDD` `±DDDMM.MMM` `±DDDMMSS.SSS` along with altitude when properly formatted IAW ISO6709, for example `+1200.00-02130.00+2321CRS_WGS_85/` Also supports the "Human Readable" format: `DD°MM′SS.SSS″N DDD°MM′SS.SSS″W` ```rust use iso6709parse::parse; let coord: geo_types::Coord = parse("N35.50W170.10+8712CRSWGS_85/").unwrap(); assert_eq!(coord.y, 35.5); ``` iso6709parse-0.1.2/benches/benchmark.rs000064400000000000000000000043041046102023000157070ustar 00000000000000use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use iso6709parse::{parse, parse_readable, parse_string_representation}; const READABLE: &str = "15°30′00.000″N 95°15′00.000″W"; pub fn iso6709readable(c: &mut Criterion) { c.bench_function("parse_readable", |b| { b.iter(|| parse_readable::(black_box(READABLE))) }); } fn bench_readable(c: &mut Criterion) { let mut group = c.benchmark_group("readable_format"); let strings = [ "15°30′00.000″N 95°15′00.000″W", "15°30′00.000″N 95°15′00.000″W 123.45m", ]; for str in strings.iter() { group.bench_with_input(BenchmarkId::new("iso6709parse", str), str, |b, str| { b.iter(|| parse::(black_box(str))) }); group.bench_with_input( BenchmarkId::new("iso6709parse_readable", str), str, |b, str| b.iter(|| parse_readable::(str)), ); group.bench_with_input(BenchmarkId::new("latlon", str), str, |b, str| { b.iter(|| latlon::parse(str)) }); } group.finish(); } fn bench_string(c: &mut Criterion) { let mut group = c.benchmark_group("string_representation_format"); let strings = [ "+12.10-021.10", //DD.DD "+12.10-021.10+2321CRSWGS_85", "+1223.101-02123.101", //DDMM.MM "+1223.101-02123.101+2321CRSWGS_85", "+122345.102-0212345.102", //DDMMSS.SSS "+122345.102-0212345.102+2321CRSWGS_85", ]; for str in strings.iter() { group.bench_with_input(BenchmarkId::new("iso6709parse", str), str, |b, str| { b.iter(|| parse::(black_box(str))) }); group.bench_with_input( BenchmarkId::new("iso6709parse_string", str), str, |b, str| b.iter(|| parse_string_representation::(black_box(str))), ); group.bench_with_input(BenchmarkId::new("latlon", str), str, |b, str| { b.iter(|| latlon::parse(black_box(str))) }); } group.finish(); } criterion_group!(benches, bench_readable, bench_string); criterion_main!(benches); iso6709parse-0.1.2/src/error.rs000064400000000000000000000007141046102023000142670ustar 00000000000000#[derive(Debug, PartialEq)] pub struct ISO6709Error(String); impl std::error::Error for ISO6709Error {} impl std::fmt::Display for ISO6709Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Failed to parse ISO6709 coordinate: {}", &self.0) } } impl From> for ISO6709Error { fn from(value: nom::error::Error<&'_ str>) -> Self { ISO6709Error(value.to_string()) } } iso6709parse-0.1.2/src/lib.rs000064400000000000000000000135051046102023000137060ustar 00000000000000use nom::character::complete::multispace0; use nom::error::ParseError; use nom::sequence::delimited; use nom::Finish; use nom::IResult; use parsers::iso6709; pub mod parsers { mod altitude; pub(crate) mod common; pub mod iso6709; mod latitude; mod longitude; } mod error; pub use crate::error::ISO6709Error; /// The struct that this library's parses create. `geo_types` `Point` and `Coord` have the `Into` traits /// implemented for this struct, so using this struct is only needed if you wish to create your own struct or /// enum that implements `From` #[derive(Debug, PartialEq, Clone)] pub struct ISO6709Coord { pub lat: f64, pub lon: f64, pub altitude: Option, } impl From for geo_types::Point { fn from(value: ISO6709Coord) -> Self { geo_types::Point::new(value.lon, value.lat) } } impl From for geo_types::Coord { fn from(value: ISO6709Coord) -> Self { geo_types::Coord { x: value.lon, y: value.lat, } } } /// Parses a string in ISO6709 human readable format into any struct that implements `From`. /// Using a normal single quote `'` in place of `′`, and a double quote `"` in place of `″` is acceptable. /// An error will be returned if the resulting coordinate exceeds 90° for latitude and 180° for longitude in either direction. /// ``` /// # use iso6709parse::parse_readable; /// let str = "15°30′00.000″N 95°15′00.000″W"; /// let geo_coord = parse_readable::(str).unwrap(); /// assert_eq!(geo_coord.x, -95.25); /// assert_eq!(geo_coord.y, 15.5); /// ``` pub fn parse_readable(str: &str) -> Result where ISO6709Coord: Into, { let (_, ((lat, lon), altitude)) = trim(iso6709::human_readable::latlong_altitude_option_parser)(str).finish()?; Ok(ISO6709Coord { lat, lon, altitude }.into()) } /// Parses a string in ISO6709 string representation format into any struct that implements `From` /// Supports the formats: /// DD.DDD /// DDMM.MMMM /// DDMMSS.SSSS /// and using either `+`/`-` or `N`/`S` and `E`/`W`. /// NOTE: digits less than 10 in the degree, minutes, or seconds column need to have a leading zero, as is IAW ISO6709 /// An error will be returned if the resulting coordinate exceeds 90° for latitude and 180° for longitude in either direction. /// ``` /// # use iso6709parse::parse_string_representation; /// let str = "N35.50W170.10+8712CRSWGS_85/"; /// let geo_coord = parse_string_representation::(str).unwrap(); /// assert_eq!(geo_coord.x, -170.1); /// assert_eq!(geo_coord.y, 35.5); /// ``` pub fn parse_string_representation(str: &str) -> Result where ISO6709Coord: Into, { let (_, ((lat, lon), altitude)) = trim(iso6709::string_expression::latlong_altitude_option_parser)(str).finish()?; Ok(ISO6709Coord { lat, lon, altitude }.into()) } /// Parse either of the two different formats. /// ```rust ///use iso6709parse::parse; /// ///let coord: geo_types::Coord = parse("N35.50W170.10+8712CRSWGS_85/").unwrap(); ///assert_eq!(coord.y, 35.5); /// ///``` pub fn parse(str: &str) -> Result where ISO6709Coord: Into, { match parse_readable(str) { Ok(x) => Ok(x), Err(_) => parse_string_representation(str), } } fn trim<'a, F, O, E>(inner: F) -> impl FnMut(&'a str) -> IResult<&'a str, O, E> where F: Fn(&'a str) -> IResult<&'a str, O, E> + 'a, E: ParseError<&'a str>, { delimited(multispace0, inner, multispace0) } #[cfg(test)] mod tests { use super::*; #[test] fn should_parse_readable_format() { let mut expected = ISO6709Coord { lat: 15.5, lon: -95.25, altitude: None, }; let coord = "15°30′00.000″N 95°15′00.000″W"; assert_eq!(parse_readable::(coord), Ok(expected.clone())); let coord = " 15°30′00.000″N 95°15′00.000″W"; assert_eq!(parse_readable::(coord), Ok(expected.clone())); let coord = " 15°30′00.000″N 95°15′00.000″W "; assert_eq!(parse_readable::(coord), Ok(expected.clone())); expected.altitude = Some(123.45); let coord = "15°30′00.000″N 95°15′00.000″W 123.45m"; assert_eq!(parse_readable::(coord), Ok(expected.clone())); let coord = " 15°30′00.000″N 95°15′00.000″W 123.45m "; assert_eq!(parse_readable::(coord), Ok(expected.clone())); } #[test] fn should_parse_string_format() { let mut expected = ISO6709Coord { lat: 35.5, lon: -170.1, altitude: None, }; let coord = "N35.50W170.10/"; assert_eq!( parse_string_representation::(coord), Ok(expected.clone()) ); let coord = " N35.50W170.10/ "; assert_eq!( parse_string_representation::(coord), Ok(expected.clone()) ); expected.altitude = Some(8712.); let coord = "N35.50W170.10+8712CRSWGS_85/"; assert_eq!( parse_string_representation::(coord), Ok(expected.clone()) ); } #[test] fn should_parse_either() { let expected = ISO6709Coord { lat: 15.5, lon: -95.25, altitude: None, }; let coord = "15°30′00.000″N 95°15′00.000″W"; assert_eq!(parse::(coord), Ok(expected.clone())); let expected = ISO6709Coord { lat: 35.5, lon: -170.1, altitude: None, }; let coord = "N35.50W170.10/"; assert_eq!(parse::(coord), Ok(expected.clone())); } } iso6709parse-0.1.2/src/parsers/altitude.rs000064400000000000000000000106471046102023000164360ustar 00000000000000use nom::branch::alt; use nom::bytes::complete::tag; use nom::bytes::complete::{is_not, take_while}; use nom::character::complete::{alpha1, digit1}; use nom::character::is_digit; use nom::combinator::map_res; use nom::combinator::value; use nom::sequence::{pair, preceded, tuple}; use nom::IResult; pub mod human_readable { use super::*; // 50°40′46.461″N 95°48′26.533″W 123.45m // 50°03′46.461″S 125°48′26.533″E 978.90m fn parse_sign(inp: &str) -> IResult<&str, f64> { let negative: IResult<&str, &str> = tag("-")(inp); match negative { Ok((rem, _)) => Ok((rem, -1.)), Err(_) => Ok((inp, 1.)), } } fn is_part_of_float(ch: char) -> bool { ch.is_ascii() && (is_digit(ch as u8) || ch == '.') } fn altitude_decimal(inp: &str) -> IResult<&str, f64> { map_res(take_while(is_part_of_float), |x: &str| x.parse::())(inp) } pub fn altitude_parser(inp: &str) -> IResult<&str, f64> { let (rem, mag) = parse_sign(inp)?; let (rem, alt) = altitude_decimal(rem)?; Ok((rem, alt * mag)) } #[allow(dead_code)] /// Follows only after using altitude_parser pub fn altitude_unit(inp: &str) -> IResult<&str, &str> { alpha1(inp) } #[cfg(test)] mod altitude_test { use super::*; #[test] fn should_parse_alt() { let inp = "978.90m"; assert_eq!(altitude_parser(inp), Ok(("m", 978.9))); let inp = "-978.90m"; assert_eq!(altitude_parser(inp), Ok(("m", -978.9))); assert_eq!(altitude_unit("m"), Ok(("", "m"))); } #[test] fn should_err_alt() { let inp = "a978.90m"; assert!(altitude_parser(inp).is_err()); } } } pub mod string_expression { use super::*; fn parse_positive(inp: &str) -> IResult<&str, f64> { value(1., tag("+"))(inp) } fn parse_negative(inp: &str) -> IResult<&str, f64> { value(-1., tag("-"))(inp) } fn parse_sign(inp: &str) -> IResult<&str, f64> { alt((parse_positive, parse_negative))(inp) } // Unsure if decimals are allowed, so we will support both fn altitude(inp: &str) -> IResult<&str, f64> { // Order matters alt((altitude_decimal, altitude_int))(inp) } fn is_part_of_float(ch: char) -> bool { ch.is_ascii() && (is_digit(ch as u8) || ch == '.') } fn altitude_decimal(inp: &str) -> IResult<&str, f64> { map_res(take_while(is_part_of_float), |x: &str| x.parse::())(inp) } fn altitude_int(inp: &str) -> IResult<&str, f64> { map_res(digit1, |x: &str| x.parse::())(inp) } fn parse_altitude_digits(inp: &str) -> IResult<&str, f64> { let (rem, (sign, altitude)) = tuple((parse_sign, altitude))(inp)?; Ok((rem, sign * altitude)) } /// Parses the string that contains altitude AND the crs. /// +2122CRSWGS_85 /// Only returns the altitude in f64 pub(crate) fn altitude_parser(altitude_with_crs: &str) -> IResult<&str, f64> { let (reference_system, (alt, _)) = pair(parse_altitude_digits, tag("CRS"))(altitude_with_crs)?; Ok((reference_system, alt)) } #[allow(dead_code)] /// Parses the string that contains altitude AND the crs. /// +2122CRSWGS_85 /// Only returns the CRS (Coordinate Reference System) pub(crate) fn crs_parser(altitude_with_crs: &str) -> IResult<&str, &str> { preceded(altitude_parser, is_not("/"))(altitude_with_crs) } #[cfg(test)] mod altitude_test { use super::*; #[test] fn should_parse_altitude() { let inp = "+2122CRSWGS_85/"; assert_eq!(altitude_parser(inp), Ok(("WGS_85/", 2122.))); let inp = "+2122.4CRSWGS_85/"; assert_eq!(altitude_parser(inp), Ok(("WGS_85/", 2122.4))); } #[test] fn should_err_altitude() { let inp = "+2122"; assert!(altitude_parser(inp).is_err()); let inp = "2122CRSWGS_85/"; assert!(altitude_parser(inp).is_err()); } #[test] fn should_parse_crs() { let inp = "+2122CRSWGS_85/"; assert_eq!(crs_parser(inp), Ok(("/", "WGS_85"))); } #[test] fn should_err_crs() { let inp = "+2122CRS"; assert!(crs_parser(inp).is_err()); } } } iso6709parse-0.1.2/src/parsers/common.rs000064400000000000000000000024601046102023000161050ustar 00000000000000use nom::branch::alt; use nom::bytes::complete::tag; use nom::character::complete::digit1; use nom::combinator::{map_res, opt, recognize}; use nom::sequence::{terminated, tuple}; use nom::IResult; #[cfg(test)] pub(crate) fn assert_float_approx( actual: IResult<&str, f64, E>, expected: f64, ) { let actual = actual.unwrap(); assert!( (actual.1 - expected).abs() < 0.0001f64, "expected: {}, actual: {}", expected, actual.1 ) } pub(crate) mod human_readable { use super::*; pub(crate) fn parse_value(inp: &str) -> IResult<&str, f64> { map_res(digit1, |x: &str| x.parse::())(inp) } pub(crate) fn parse_degree(inp: &str) -> IResult<&str, f64> { terminated(parse_value, tag("°"))(inp) } pub(crate) fn parse_minutes(inp: &str) -> IResult<&str, f64> { terminated(parse_value, alt((tag("'"), tag("′"))))(inp) } pub(crate) fn parse_seconds_with_decimal(inp: &str) -> IResult<&str, f64> { map_res( recognize(tuple((digit1, opt(tuple((tag("."), digit1)))))), |x: &str| x.parse::(), )(inp) } pub(crate) fn parse_seconds(inp: &str) -> IResult<&str, f64> { terminated(parse_seconds_with_decimal, alt((tag("\""), tag("″"))))(inp) } } iso6709parse-0.1.2/src/parsers/iso6709.rs000064400000000000000000000167201046102023000157410ustar 00000000000000use nom::IResult; pub mod human_readable { use super::*; use crate::parsers::altitude::human_readable::*; use crate::parsers::latitude::human_readable::*; use crate::parsers::longitude::human_readable::*; use nom::character::complete::space1; use nom::combinator::opt; use nom::sequence::{preceded, separated_pair, tuple}; /// Parser to obtain lat long /// /// /// ``` /// # use iso6709parse::parsers::iso6709::human_readable::latlong_parser; /// let coord = "15°30′00.000″N 95°15′00.000″W"; /// assert_eq!(latlong_parser(coord), Ok(("", (15.5, -95.25)))); /// /// let coord = "15°30′00.000″N 95°15′00.000″W 123.45m"; /// assert_eq!(latlong_parser(coord), Ok((" 123.45m", (15.5, -95.25)))); /// ``` /// pub fn latlong_parser(inp: &str) -> IResult<&str, (f64, f64)> { separated_pair(latitude_parser, space1, longitude_parser)(inp) } /// Parser to obtain lat long and altitude. Note that the lat, long are within their own tuple, inside the output tuple. /// Since the `CRS` statement is required for altitude, it is parsed and discarded from the remaining string /// /// /// ``` /// # use iso6709parse::parsers::iso6709::human_readable::latlong_altitude_parser; /// let coord = "15°30′00.000″N 95°15′00.000″W"; /// assert!(latlong_altitude_parser(coord).is_err()); /// /// let coord = "15°30′00.000″N 95°15′00.000″W 123.45m"; /// assert_eq!(latlong_altitude_parser(coord), Ok(("m", ((15.5, -95.25), 123.45)))); /// ``` /// pub fn latlong_altitude_parser(inp: &str) -> IResult<&str, ((f64, f64), f64)> { separated_pair(latlong_parser, space1, altitude_parser)(inp) } /// Parser to obtain lat long and altitude if the altitude is present. Note that the lat, long are within their own tuple, inside the output tuple. /// Since the `CRS` statement is required for altitude, it is parsed and discarded from the remaining string /// /// /// ``` /// # use iso6709parse::parsers::iso6709::human_readable::latlong_altitude_option_parser; /// let coord = "15°30′00.000″N 95°15′00.000″W"; /// assert_eq!(latlong_altitude_option_parser(coord), Ok(("", ((15.5, -95.25), None)))); /// /// let coord = "15°30′00.000″N 95°15′00.000″W 123.45m"; /// assert_eq!(latlong_altitude_option_parser(coord), Ok(("m", ((15.5, -95.25), Some(123.45))))); /// ``` /// pub fn latlong_altitude_option_parser(inp: &str) -> IResult<&str, ((f64, f64), Option)> { tuple((latlong_parser, opt(preceded(space1, altitude_parser))))(inp) } #[cfg(test)] mod human_readable_tests { use super::*; #[test] fn should_parse_readable() { let coord = "15°30′00.000″N 95°15′00.000″W"; assert_eq!(latlong_parser(coord), Ok(("", (15.5, -95.25)))); let coord = "15°30′00.000″N 95°15′00.000″W 123.45m"; assert_eq!(latlong_parser(coord), Ok((" 123.45m", (15.5, -95.25)))); } #[test] fn should_parse_readable_altitude() { let coord = "15°30′00.000″N 95°15′00.000″W"; assert!(latlong_altitude_parser(coord).is_err()); let coord = "15°30′00.000″N 95°15′00.000″W 123.45m"; assert_eq!( latlong_altitude_parser(coord), Ok(("m", ((15.5, -95.25), 123.45))) ); } } } pub mod string_expression { use super::*; pub(crate) use crate::parsers::altitude::string_expression::altitude_parser; pub use crate::parsers::latitude::string_expression::latitude_parser; pub use crate::parsers::longitude::string_expression::longitude_parser; use nom::combinator::opt; use nom::sequence::tuple; /// Parser to obtain lat long /// /// /// ``` /// # use iso6709parse::parsers::iso6709::string_expression::latlong_parser; /// let coord = "+1200.00-02130.00"; /// assert_eq!(latlong_parser(coord), Ok(("", (12.0, -21.5)))); /// /// let coord = "+1200.00-02130.00+2321CRS_WGS_85/"; /// assert_eq!(latlong_parser(coord), Ok(("+2321CRS_WGS_85/", (12.0, -21.5)))); /// ``` /// pub fn latlong_parser(inp: &str) -> IResult<&str, (f64, f64)> { tuple((latitude_parser, longitude_parser))(inp) } /// Parser to obtain lat long and altitude. Note that the lat, long are within their own tuple, inside the output tuple. /// Since the `CRS` statement is required for altitude, it is parsed and discarded from the remaining string /// /// /// ``` /// # use iso6709parse::parsers::iso6709::string_expression::latlong_altitude_parser; /// let coord = "+1200.00-02130.00"; /// assert!(latlong_altitude_parser(coord).is_err()); /// /// let coord = "+1200.00-02130.00+2321CRSWGS_85"; /// assert_eq!(latlong_altitude_parser(coord), Ok(("WGS_85", ((12.0, -21.5), 2321.0)))); /// ``` /// pub fn latlong_altitude_parser(inp: &str) -> IResult<&str, ((f64, f64), f64)> { tuple((latlong_parser, altitude_parser))(inp) } // Parser to obtain lat long and, if exists, the altitude. Note that the lat, long are within their own tuple, inside the output tuple. /// Since the `CRS` statement is required for altitude, it is parsed and discarded from the remaining string /// /// /// ``` /// # use iso6709parse::parsers::iso6709::string_expression::latlong_altitude_option_parser; /// let coord = "+1200.00-02130.00"; /// assert_eq!(latlong_altitude_option_parser(coord), Ok(("", ((12.0, -21.5), None)))); /// /// let coord = "+1200.00-02130.00+2321CRSWGS_85"; /// assert_eq!(latlong_altitude_option_parser(coord), Ok(("WGS_85", ((12.0, -21.5), Some(2321.0))))); /// ``` /// pub fn latlong_altitude_option_parser(inp: &str) -> IResult<&str, ((f64, f64), Option)> { tuple((latlong_parser, opt(altitude_parser)))(inp) } #[cfg(test)] mod string_expression_tests { use super::*; #[test] fn should_parse_latlong() { assert_eq!(latlong_parser("+35.50+170.00"), Ok(("", (35.5, 170.0)))); assert_eq!(latlong_parser("+35.50-170.10"), Ok(("", (35.5, -170.1)))); assert_eq!(latlong_parser("+35-170"), Ok(("", (35., -170.)))); assert_eq!(latlong_parser("+05.50-070.10"), Ok(("", (5.5, -70.1)))); assert_eq!(latlong_parser("N35.50W170.10"), Ok(("", (35.5, -170.1)))); assert_eq!(latlong_parser("+3530+17030"), Ok(("", (35.5, 170.5)))); assert_eq!(latlong_parser("+3530.0-17030.0"), Ok(("", (35.5, -170.5)))); assert_eq!(latlong_parser("+05.50-070.10"), Ok(("", (5.5, -70.1)))); assert_eq!( latlong_parser("N35.50W170.10+8712CRSWGS_85/"), Ok(("+8712CRSWGS_85/", (35.5, -170.1))) ) } #[test] fn should_parse_latlong_altitude() { assert_eq!( latlong_altitude_parser("N35.50W170.10+8712CRSWGS_85/"), Ok(("WGS_85/", ((35.5, -170.1), 8712.))) ); assert_eq!( latlong_altitude_parser("N35.50W170.10-8712CRSWGS_85/"), Ok(("WGS_85/", ((35.5, -170.1), -8712.))) ); assert_eq!( latlong_altitude_parser("N35.50W170.10-8712.5CRSWGS_85/"), Ok(("WGS_85/", ((35.5, -170.1), -8712.5))) ) } } } iso6709parse-0.1.2/src/parsers/latitude.rs000064400000000000000000000240441046102023000164320ustar 00000000000000#![allow(dead_code)] use nom::branch::alt; use nom::bytes::complete::tag; use nom::bytes::complete::take_while_m_n; use nom::character::complete::{digit0, u8}; use nom::character::is_digit; use nom::combinator::{map, opt, value}; use nom::combinator::{map_parser, map_res, recognize}; use nom::error::ParseError; use nom::sequence::tuple; use nom::IResult; pub mod human_readable { use super::*; use crate::parsers::common::human_readable::*; // 50°40′46.461″N 95°48′26.533″W 123.45m // 50°03′46.461″S 125°48′26.533″E 978.90m fn parse_north(inp: &str) -> IResult<&str, f64> { value(1., tag("N"))(inp) } fn parse_south(inp: &str) -> IResult<&str, f64> { value(-1., tag("S"))(inp) } fn parse_north_or_south(inp: &str) -> IResult<&str, f64> { alt((parse_north, parse_south))(inp) } pub fn latitude_parser(inp: &str) -> IResult<&str, f64> { let (rem, deg) = parse_degree(inp)?; let (rem, min) = parse_minutes(rem)?; let (rem, sec) = parse_seconds(rem)?; let (rem, mag) = parse_north_or_south(rem)?; let value = deg + min / 60. + sec / 3600.; if value > 90.0 { Err(nom::Err::Failure(nom::error::Error::new( inp, nom::error::ErrorKind::Fail, ))) } else { Ok((rem, mag * value)) } } #[cfg(test)] mod lat_tests { use super::*; use crate::parsers::common::assert_float_approx; #[test] fn should_parse_latitude() { let inp = "50°40′46.461″N 95°48′26.533″W 123.45m"; assert_float_approx(latitude_parser(inp), 50.679573); assert_eq!(latitude_parser(inp).unwrap().0, " 95°48′26.533″W 123.45m"); let inp = "50°40'46.461\"N 95°48′26.533″W 123.45m"; assert_float_approx(latitude_parser(inp), 50.679573); let inp = "00°40′46.461″N 95°48′26.533″W 123.45m"; assert_float_approx(latitude_parser(inp), 0.679573); let inp = "50°40′46.461″S 95°48′26.533″W 123.45m"; assert_float_approx(latitude_parser(inp), -50.679573); let inp = "50°40'46.461\"S 95°48′26.533″W 123.45m"; assert_float_approx(latitude_parser(inp), -50.679573); let inp = "50°40'46\"S 95°48′26.533″W 123.45m"; assert_float_approx(latitude_parser(inp), -50.679444); let inp = "90°00'00.00\"S 95°48′26.533″W 123.45m"; assert_float_approx(latitude_parser(inp), -90.); let inp = "90°00'00.00\"N 95°48′26.533″W 123.45m"; assert_float_approx(latitude_parser(inp), 90.); let inp = "00°00'00.00\"N 95°48′26.533″W 123.45m"; assert_float_approx(latitude_parser(inp), 0.); let inp = "00°00'00.00\"S 95°48′26.533″W 123.45m"; assert_float_approx(latitude_parser(inp), 0.); } #[test] fn should_err_latitude() { let inp = "50.40′46.461″N 95°48′26.533″W 123.45m"; assert!(latitude_parser(inp).is_err()); let inp = "50.40′46.461.N 95°48′26.533″W 123.45m"; assert!(latitude_parser(inp).is_err()); let inp = "50°40.46.461″N 95°48′26.533″W 123.45m"; assert!(latitude_parser(inp).is_err()); let inp = "90°40′46.461″N 95°48′26.533″W 123.45m"; assert!(latitude_parser(inp).is_err()); } } } pub mod string_expression { use super::*; fn parse_north(inp: &str) -> IResult<&str, f64> { value(1., alt((tag("N"), tag("+"))))(inp) } fn parse_south(inp: &str) -> IResult<&str, f64> { value(-1., alt((tag("S"), tag("-"))))(inp) } fn parse_north_or_south(inp: &str) -> IResult<&str, f64> { alt((parse_north, parse_south))(inp) } fn is_char_digit(char: char) -> bool { char.is_ascii() && is_digit(char as u8) } fn parse_two<'a, F, O, E>(inner: F) -> impl FnMut(&'a str) -> IResult<&'a str, O, E> where F: Fn(&'a str) -> IResult<&'a str, O, E> + 'a, E: ParseError<&'a str>, { map_parser(take_while_m_n(2, 2, is_char_digit), inner) } fn parse_degree_integer(inp: &str) -> IResult<&str, f64> { map(parse_two(u8), |x| x as f64)(inp) } fn parse_degree_min_integer(inp: &str) -> IResult<&str, f64> { let (rem, (degrees, minutes)) = tuple((parse_two(u8), parse_two(u8)))(inp)?; if minutes >= 60 { Err(nom::Err::Failure(nom::error::Error::new( inp, nom::error::ErrorKind::Fail, ))) } else { Ok((rem, (degrees as f64) + (minutes as f64 / 60.))) } } fn parse_degree_min_sec_integer(inp: &str) -> IResult<&str, f64> { let (rem, (degrees, minutes, seconds)) = tuple((parse_two(u8), parse_two(u8), parse_two(u8)))(inp)?; if minutes >= 60 || seconds >= 60 { Err(nom::Err::Failure(nom::error::Error::new( inp, nom::error::ErrorKind::Fail, ))) } else { Ok(( rem, (degrees as f64) + (minutes as f64 / 60.) + (seconds as f64 / 3600.), )) } } fn parse_decimal(inp: &str) -> IResult<&str, f64> { map_res(recognize(tuple((tag("."), digit0))), |x: &str| { x.parse::() })(inp) } fn parse_degree(inp: &str) -> IResult<&str, f64> { let (decimalstr, int) = parse_degree_integer(inp)?; let (rem, dec) = opt(parse_decimal)(decimalstr)?; Ok((rem, int + dec.unwrap_or(0.))) } fn parse_degree_minute(inp: &str) -> IResult<&str, f64> { let (decimalstr, int) = parse_degree_min_integer(inp)?; let (rem, dec) = opt(parse_decimal)(decimalstr)?; Ok((rem, int + dec.unwrap_or(0.) / 60.)) } fn parse_degree_minute_second(inp: &str) -> IResult<&str, f64> { let (decimalstr, int) = parse_degree_min_sec_integer(inp)?; let (rem, dec) = opt(parse_decimal)(decimalstr)?; Ok((rem, int + dec.unwrap_or(0.) / 3600.)) } /// Nom style parser for latitude. The beginning of the string slice must be the start of latitude. /// Returns Err if failed to parse, or latitude is greater than +/-90.0 pub fn latitude_parser(inp: &str) -> IResult<&str, f64> { let (lat, mag) = parse_north_or_south(inp)?; let (rem, value) = alt(( parse_degree_minute_second, parse_degree_minute, parse_degree, ))(lat)?; if value > 90.0 { Err(nom::Err::Failure(nom::error::Error::new( lat, nom::error::ErrorKind::Fail, ))) } else { Ok((rem, mag * value)) } } #[cfg(test)] mod lat_tests { use super::latitude_parser; use super::parse_north_or_south; use crate::parsers::common::assert_float_approx; #[test] fn should_parse_direction() { assert_eq!(parse_north_or_south("N"), Ok(("", 1.))); assert_eq!(parse_north_or_south("+"), Ok(("", 1.))); assert_eq!(parse_north_or_south("S"), Ok(("", -1.))); assert_eq!(parse_north_or_south("-"), Ok(("", -1.))); assert_eq!(parse_north_or_south("-123.123"), Ok(("123.123", -1.))); assert!(parse_north_or_south("n").is_err()); } #[test] fn should_parse_dd_ddd() { assert_eq!(latitude_parser("+45.45"), Ok(("", 45.45))); assert_eq!(latitude_parser("N45.45"), Ok(("", 45.45))); assert_eq!(latitude_parser("-45.45"), Ok(("", -45.45))); assert_eq!(latitude_parser("S45.45"), Ok(("", -45.45))); assert_eq!(latitude_parser("S45"), Ok(("", -45.))); assert_eq!(latitude_parser("S45.1234"), Ok(("", -45.1234))); //Padding assert_eq!(latitude_parser("S45.45"), Ok(("", -45.45))); assert_eq!(latitude_parser("+05.45"), Ok(("", 5.45))); assert_eq!(latitude_parser("+05.05"), Ok(("", 5.05))); //Poles assert_eq!(latitude_parser("+90.0"), Ok(("", 90.0))); assert_eq!(latitude_parser("N90"), Ok(("", 90.0))); assert_eq!(latitude_parser("S90"), Ok(("", -90.0))); assert_eq!(latitude_parser("-90.0"), Ok(("", -90.0))); } #[test] fn should_error_dd_ddd() { assert!(latitude_parser("45.45").is_err()); assert!(latitude_parser("n45.45").is_err()); assert!(latitude_parser("+5.45").is_err()); assert!(latitude_parser("North45.45").is_err()); assert!(latitude_parser("145.45").is_err()); assert!(latitude_parser("N99.45").is_err()); assert!(latitude_parser("+90.1").is_err()); assert!(latitude_parser("-90.1").is_err()); } #[test] fn should_parse_ddmm_mmm() { assert_float_approx(latitude_parser("+4520.30"), 45.33833); assert_float_approx(latitude_parser("S4520.30"), -45.338333); assert_float_approx(latitude_parser("S4520.12304"), -45.335384); assert_float_approx(latitude_parser("S4520"), -45.33333); assert_float_approx(latitude_parser("S4500"), -45.); } #[test] fn should_error_ddmm_mmm() { assert!(latitude_parser("4545.45").is_err()); assert!(latitude_parser("N4560.45").is_err()); assert!(latitude_parser("N4560").is_err()); assert!(latitude_parser("N4590.45").is_err()); } #[test] fn should_parse_ddmmss_sss() { assert_float_approx(latitude_parser("+452018"), 45.338333); assert_float_approx(latitude_parser("S452018"), -45.338333); assert_float_approx(latitude_parser("S452000"), -45.33333); assert_float_approx(latitude_parser("S450000"), -45.); assert_float_approx(latitude_parser("S452035.1528"), -45.343098); } } } iso6709parse-0.1.2/src/parsers/longitude.rs000064400000000000000000000233041046102023000166070ustar 00000000000000use nom::branch::alt; use nom::bytes::complete::tag; use nom::bytes::complete::take_while_m_n; use nom::character::complete::{digit0, u8}; use nom::character::is_digit; use nom::combinator::{map, opt, value}; use nom::combinator::{map_parser, map_res, recognize}; use nom::error::ParseError; use nom::sequence::tuple; use nom::IResult; pub mod human_readable { use super::*; use crate::parsers::common::human_readable::*; // 50°40′46.461″N 95°48′26.533″W 123.45m // 50°03′46.461″S 125°48′26.533″E 978.90m fn parse_east(inp: &str) -> IResult<&str, f64> { value(1., tag("E"))(inp) } fn parse_west(inp: &str) -> IResult<&str, f64> { value(-1., tag("W"))(inp) } fn parse_east_or_west(inp: &str) -> IResult<&str, f64> { alt((parse_east, parse_west))(inp) } pub fn longitude_parser(inp: &str) -> IResult<&str, f64> { let (rem, deg) = parse_degree(inp)?; let (rem, min) = parse_minutes(rem)?; let (rem, sec) = parse_seconds(rem)?; let (rem, mag) = parse_east_or_west(rem)?; let value = deg + min / 60. + sec / 3600.; if value > 180.0 { Err(nom::Err::Failure(nom::error::Error::new( inp, nom::error::ErrorKind::Fail, ))) } else { Ok((rem, mag * value)) } } #[cfg(test)] mod lon_test { use super::*; use crate::parsers::common::assert_float_approx; #[test] fn should_parse_logitude() { let inp = "95°48′26.533″W 123.45m"; assert_float_approx(longitude_parser(inp), -95.80737); let inp = "95°48′26.533″E 123.45m"; assert_float_approx(longitude_parser(inp), 95.80737); let inp = "95°48'26.533″W 123.45m"; assert_float_approx(longitude_parser(inp), -95.80737); let inp = r#"95°48′26.533"W 123.45m"#; assert_float_approx(longitude_parser(inp), -95.80737); let inp = "95°48′26.533″W 123.45m"; assert_float_approx(longitude_parser(inp), -95.80737); let inp = "180°00′0″W 123.45m"; assert_float_approx(longitude_parser(inp), -180.); let inp = "180°00′0″E 123.45m"; assert_float_approx(longitude_parser(inp), 180.); } #[test] fn should_err_longitude() { let inp = "95.48′26.533″W 123.45m"; assert!(longitude_parser(inp).is_err()); let inp = "95°48.26.533″W 123.45m"; assert!(longitude_parser(inp).is_err()); let inp = "95°48′26.533.W 123.45m"; assert!(longitude_parser(inp).is_err()); let inp = "180°′1.″W 123.45m"; assert!(longitude_parser(inp).is_err()); let inp = "180°′1.″E 123.45m"; assert!(longitude_parser(inp).is_err()); let inp = "95.48′26.533″ 123.45m"; assert!(longitude_parser(inp).is_err()); } } } pub mod string_expression { use super::*; fn parse_east(inp: &str) -> IResult<&str, f64> { value(1., alt((tag("E"), tag("+"))))(inp) } fn parse_west(inp: &str) -> IResult<&str, f64> { value(-1., alt((tag("W"), tag("-"))))(inp) } fn parse_east_or_west(inp: &str) -> IResult<&str, f64> { alt((parse_east, parse_west))(inp) } fn is_char_digit(char: char) -> bool { char.is_ascii() && is_digit(char as u8) } fn parse_two<'a, F, O, E>(inner: F) -> impl FnMut(&'a str) -> IResult<&'a str, O, E> where F: Fn(&'a str) -> IResult<&'a str, O, E> + 'a, E: ParseError<&'a str>, { map_parser(take_while_m_n(2, 2, is_char_digit), inner) } fn parse_three<'a, F, O, E>(inner: F) -> impl FnMut(&'a str) -> IResult<&'a str, O, E> where F: Fn(&'a str) -> IResult<&'a str, O, E> + 'a, E: ParseError<&'a str>, { map_parser(take_while_m_n(3, 3, is_char_digit), inner) } fn parse_degree_integer(inp: &str) -> IResult<&str, f64> { map(parse_three(u8), |x| x as f64)(inp) } fn parse_degree_min_integer(inp: &str) -> IResult<&str, f64> { let (rem, (degrees, minutes)) = tuple((parse_three(u8), parse_two(u8)))(inp)?; if minutes >= 60 { Err(nom::Err::Failure(nom::error::Error::new( inp, nom::error::ErrorKind::Fail, ))) } else { Ok((rem, (degrees as f64) + (minutes as f64 / 60.))) } } fn parse_degree_min_sec_integer(inp: &str) -> IResult<&str, f64> { let (rem, (degrees, minutes, seconds)) = tuple((parse_three(u8), parse_two(u8), parse_two(u8)))(inp)?; if minutes >= 60 || seconds >= 60 { Err(nom::Err::Failure(nom::error::Error::new( inp, nom::error::ErrorKind::Fail, ))) } else { Ok(( rem, (degrees as f64) + (minutes as f64 / 60.) + (seconds as f64 / 3600.), )) } } fn parse_decimal(inp: &str) -> IResult<&str, f64> { map_res(recognize(tuple((tag("."), digit0))), |x: &str| { x.parse::() })(inp) } fn parse_degree(inp: &str) -> IResult<&str, f64> { let (decimalstr, int) = parse_degree_integer(inp)?; let (rem, dec) = opt(parse_decimal)(decimalstr)?; Ok((rem, int + dec.unwrap_or(0.))) } fn parse_degree_minute(inp: &str) -> IResult<&str, f64> { let (decimalstr, int) = parse_degree_min_integer(inp)?; let (rem, dec) = opt(parse_decimal)(decimalstr)?; Ok((rem, int + dec.unwrap_or(0.) / 60.)) } fn parse_degree_minute_second(inp: &str) -> IResult<&str, f64> { let (decimalstr, int) = parse_degree_min_sec_integer(inp)?; let (rem, dec) = opt(parse_decimal)(decimalstr)?; Ok((rem, int + dec.unwrap_or(0.) / 3600.)) } pub fn longitude_parser(inp: &str) -> IResult<&str, f64> { let (lat, mag) = parse_east_or_west(inp)?; // Order matters for the next line! let (rem, value) = alt(( parse_degree_minute_second, parse_degree_minute, parse_degree, ))(lat)?; if value > 180.0 { Err(nom::Err::Failure(nom::error::Error::new( lat, nom::error::ErrorKind::Fail, ))) } else { Ok((rem, mag * value)) } } #[cfg(test)] mod long_tests { use super::longitude_parser; use nom::IResult; fn assert_float_no_remaining( expected: IResult<&str, f64, E>, actual: f64, ) { let expected = expected.unwrap(); assert!((expected.1 - actual).abs() < 0.0001f64); } #[test] fn should_parse_ddd_ddd() { assert_eq!(longitude_parser("+145.45"), Ok(("", 145.45))); assert_eq!(longitude_parser("E145.45"), Ok(("", 145.45))); assert_eq!(longitude_parser("-145.45"), Ok(("", -145.45))); assert_eq!(longitude_parser("W145.45"), Ok(("", -145.45))); assert_eq!(longitude_parser("W145"), Ok(("", -145.))); assert_eq!(longitude_parser("W145.1234"), Ok(("", -145.1234))); //Padding assert_eq!(longitude_parser("W045.45"), Ok(("", -45.45))); assert_eq!(longitude_parser("+005.45"), Ok(("", 5.45))); assert_eq!(longitude_parser("+005.05"), Ok(("", 5.05))); //Meridan assert_eq!(longitude_parser("+180.0"), Ok(("", 180.0))); assert_eq!(longitude_parser("E180"), Ok(("", 180.0))); assert_eq!(longitude_parser("W180"), Ok(("", -180.0))); assert_eq!(longitude_parser("-180.0"), Ok(("", -180.0))); } #[test] fn should_error_dd_ddd() { assert!(longitude_parser("45.45").is_err()); assert!(longitude_parser("w45.45").is_err()); assert!(longitude_parser("+5.45").is_err()); assert!(longitude_parser("West45.45").is_err()); assert!(longitude_parser("145.45").is_err()); assert!(longitude_parser("N129.45").is_err()); assert!(longitude_parser("+180.1").is_err()); assert!(longitude_parser("-180.1").is_err()); } #[test] fn should_parse_ddmm_mmm() { assert_float_no_remaining(longitude_parser("+14520.30"), 145.338333); assert_float_no_remaining(longitude_parser("W14520.30"), -145.338333); assert_float_no_remaining(longitude_parser("W14520.12304"), -145.335384); assert_float_no_remaining(longitude_parser("W14520"), -145.33333); assert_float_no_remaining(longitude_parser("W14500"), -145.); } #[test] fn should_error_ddmm_mmm() { assert!(longitude_parser("4545.45").is_err()); assert!(longitude_parser("N4560.45").is_err()); assert!(longitude_parser("N4560").is_err()); assert!(longitude_parser("N4590.45").is_err()); assert!( longitude_parser("N45a5.45").is_err(), "{:?}", longitude_parser("N45a5.45") ); } #[test] fn should_parse_dddmmss_sss() { assert_float_no_remaining(longitude_parser("+1452018"), 145.338333); assert_float_no_remaining(longitude_parser("W1452018"), -145.338333); assert_float_no_remaining(longitude_parser("W1452000"), -145.33333); assert_float_no_remaining(longitude_parser("W1450000"), -145.); assert_float_no_remaining(longitude_parser("W1452035.1528"), -145.343098); } } }