diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..1334eb7 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: "weekly" \ No newline at end of file diff --git a/.github/workflows/ci-version.yml b/.github/workflows/ci-version.yml index d3454ba..bb2857d 100644 --- a/.github/workflows/ci-version.yml +++ b/.github/workflows/ci-version.yml @@ -7,7 +7,7 @@ on: env: CARGO_TERM_COLOR: always - + jobs: tests: strategy: @@ -20,15 +20,37 @@ jobs: toolchain: - stable - nightly - name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} + features: + - + - --no-default-features + name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }}) + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + toolchain: ${{ matrix.toolchain }} + - run: cargo test --release ${{ matrix.features }} + - run: cargo doc --release ${{ matrix.features }} + + MSRV: + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + - macos-latest + - windows-latest + toolchain: + - 1.57 + features: + - + - --no-default-features + name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }}) runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + - uses: actions-rust-lang/setup-rust-toolchain@v1 with: - profile: minimal toolchain: ${{ matrix.toolchain }} - override: true - - run: cargo build --release - - run: cargo test --release - - run: cargo doc --release \ No newline at end of file + - run: cargo test --release --lib --bins ${{ matrix.features }} \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a4d8916..8670c90 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,32 +1,27 @@ name: CI -on: [push, pull_request] +on: [ push, pull_request ] env: CARGO_TERM_COLOR: always - + jobs: rustfmt: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + - uses: actions-rust-lang/setup-rust-toolchain@v1 with: - profile: minimal toolchain: nightly - override: true components: rustfmt - - run: cargo fmt -- --check - + - uses: actions-rust-lang/rustfmt@v1 + clippy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + - uses: actions-rust-lang/setup-rust-toolchain@v1 with: - profile: minimal - toolchain: nightly - override: true components: clippy - run: cargo clippy --all-targets --all-features -- -D warnings @@ -41,15 +36,37 @@ jobs: toolchain: - stable - nightly - name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} + features: + - + - --no-default-features + name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }}) + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + toolchain: ${{ matrix.toolchain }} + - run: cargo test ${{ matrix.features }} + - run: cargo doc ${{ matrix.features }} + + MSRV: + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + - macos-latest + - windows-latest + toolchain: + - 1.57 + features: + - + - --no-default-features + name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }}) runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + - uses: actions-rust-lang/setup-rust-toolchain@v1 with: - profile: minimal toolchain: ${{ matrix.toolchain }} - override: true - - run: cargo build - - run: cargo test - - run: cargo doc \ No newline at end of file + - run: cargo test --lib --bins ${{ matrix.features }} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 244d369..f19ca82 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,16 +1,21 @@ [package] name = "base64-url" -version = "1.4.13" +version = "2.0.1" authors = ["Magic Len "] edition = "2021" +rust-version = "1.57" repository = "https://github.com/magiclen/base64-url" homepage = "https://magiclen.org/base64-url" keywords = ["decode", "base64", "encode", "utf8", "url"] -categories = ["encoding"] +categories = ["no-std", "encoding"] description = "Base64 encode, decode, escape and unescape for URL applications." -readme = "README.md" license = "MIT" include = ["src/**/*", "Cargo.toml", "README.md", "LICENSE"] [dependencies] -base64 = "0.13" \ No newline at end of file +base64 = { version = "0.21", default-features = false, features = ["alloc"] } + +[features] +default = [] + +std = [] # deprecated \ No newline at end of file diff --git a/rustfmt.toml b/rustfmt.toml index f323f5f..04d8a8b 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,15 +1,66 @@ +# array_width = 60 +# attr_fn_like_width = 70 +binop_separator = "Front" +blank_lines_lower_bound = 0 +blank_lines_upper_bound = 1 brace_style = "PreferSameLine" -enum_discrim_align_threshold = 100 -force_explicit_abi = false -force_multiline_blocks = true +# chain_width = 60 +color = "Auto" +# comment_width = 100 +condense_wildcard_suffixes = true +control_brace_style = "AlwaysSameLine" +empty_item_single_line = true +enum_discrim_align_threshold = 80 +error_on_line_overflow = false +error_on_unformatted = false +# fn_call_width = 60 +fn_params_layout = "Tall" +fn_single_line = false +force_explicit_abi = true +force_multiline_blocks = false format_code_in_doc_comments = true +doc_comment_code_block_width = 80 +format_generated_files = true format_macro_matchers = true +format_macro_bodies = true +skip_macro_invocations = [] +format_strings = true +hard_tabs = false +hex_literal_case = "Upper" +imports_indent = "Block" +imports_layout = "Mixed" +indent_style = "Block" +inline_attribute_width = 0 +match_arm_blocks = true +match_arm_leading_pipes = "Never" +match_block_trailing_comma = true max_width = 100 +merge_derives = true +imports_granularity = "Crate" newline_style = "Unix" +normalize_comments = false normalize_doc_attributes = true overflow_delimited_expr = true +remove_nested_parens = true reorder_impl_items = true +reorder_imports = true +group_imports = "StdExternalCrate" +reorder_modules = true +short_array_element_width_threshold = 10 +# single_line_if_else_max_width = 50 +space_after_colon = true +space_before_colon = false +spaces_around_ranges = false +struct_field_align_threshold = 80 struct_lit_single_line = false +# struct_lit_width = 18 +# struct_variant_width = 35 +tab_spaces = 4 +trailing_comma = "Vertical" +trailing_semicolon = true +type_punctuation_density = "Wide" use_field_init_shorthand = true -use_small_heuristics = "Off" -use_try_shorthand = true \ No newline at end of file +use_small_heuristics = "Max" +use_try_shorthand = true +where_single_line = false +wrap_comments = false \ No newline at end of file diff --git a/src/decode.rs b/src/decode.rs index 8d0b963..29c558a 100644 --- a/src/decode.rs +++ b/src/decode.rs @@ -1,7 +1,11 @@ +use alloc::vec::Vec; + +use base64::Engine; + /// Decode a Base64-URL string to data. #[inline] pub fn decode>(input: &T) -> Result, base64::DecodeError> { - base64::decode_config(input, base64::URL_SAFE_NO_PAD) + base64::engine::general_purpose::URL_SAFE_NO_PAD.decode(input) } /// Decode a Base64-URL string to data and directly store into a mutable `Vec` reference by concatenating them and return the slice of the decoded data. @@ -9,7 +13,7 @@ pub fn decode>(input: &T) -> Result, base64::Dec pub fn decode_to_vec<'a, T: ?Sized + AsRef<[u8]>>( input: &T, output: &'a mut Vec, -) -> Result<&'a [u8], base64::DecodeError> { +) -> Result<&'a [u8], base64::DecodeSliceError> { let bytes = input.as_ref(); let current_length = output.len(); @@ -37,31 +41,8 @@ pub fn decode_to_vec<'a, T: ?Sized + AsRef<[u8]>>( pub fn decode_to_slice<'a, T: ?Sized + AsRef<[u8]>>( input: &T, output: &'a mut [u8], -) -> Result<&'a [u8], base64::DecodeError> { - let length = base64::decode_config_slice(input, base64::URL_SAFE_NO_PAD, output)?; +) -> Result<&'a [u8], base64::DecodeSliceError> { + let length = base64::engine::general_purpose::URL_SAFE_NO_PAD.decode_slice(input, output)?; Ok(&output[..length]) } - -#[deprecated(since = "1.4.0", note = "Please use the `decode_to_vec` function instead")] -/// Decode a Base64-URL string to data and directly store into a Vec instance by concatenating them. -#[inline] -pub fn decode_and_push_to_vec>( - input: &T, - mut output: Vec, -) -> Result, base64::DecodeError> { - decode_to_vec(input, &mut output)?; - - Ok(output) -} - -#[deprecated(since = "1.4.0", note = "Please use the `decode_to_vec` function instead")] -/// Decode a Base64-URL string to data and directly store into a mutable Vec reference by concatenating them. -pub fn decode_and_push_to_vec_mut>( - input: &T, - output: &mut Vec, -) -> Result<(), base64::DecodeError> { - decode_to_vec(input, output)?; - - Ok(()) -} diff --git a/src/encode.rs b/src/encode.rs index a01b8d4..c5ca543 100644 --- a/src/encode.rs +++ b/src/encode.rs @@ -1,9 +1,12 @@ -use std::str::from_utf8_unchecked; +use alloc::{string::String, vec::Vec}; +use core::str::from_utf8_unchecked; + +use base64::Engine; /// Encode data to a Base64-URL string. #[inline] pub fn encode>(input: &T) -> String { - base64::encode_config(input, base64::URL_SAFE_NO_PAD) + base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(input) } /// Encode data to a Base64-URL string and directly store to a mutable `String` reference by concatenating them and return the slice of the Base64-URL string. It is usually for generating a URL. @@ -30,7 +33,7 @@ pub fn encode_to_vec<'a, T: ?Sized + AsRef<[u8]>>(input: &T, output: &'a mut Vec output.set_len(current_length + base64_length); } - let base64_length = encode_to_slice(bytes, &mut output[current_length..]).len(); + let base64_length = encode_to_slice(bytes, &mut output[current_length..]).unwrap().len(); unsafe { output.set_len(current_length + base64_length); @@ -41,35 +44,11 @@ pub fn encode_to_vec<'a, T: ?Sized + AsRef<[u8]>>(input: &T, output: &'a mut Vec /// Encode data to a Base64-URL string to a slice and return the slice with a valid length. #[inline] -pub fn encode_to_slice<'a, T: ?Sized + AsRef<[u8]>>(input: &T, output: &'a mut [u8]) -> &'a [u8] { - let length = base64::encode_config_slice(input, base64::URL_SAFE_NO_PAD, output); - - &output[..length] -} - -#[deprecated(since = "1.4.0", note = "Please use the `encode_to_slice` function instead")] -/// Encode data to a Base64-URL string into a slice and return the valid length. -#[inline] -pub fn encode_and_store_to_slice>(input: &T, output: &mut [u8]) -> usize { - encode_to_slice(input, output).len() -} - -#[deprecated(since = "1.4.0", note = "Please use the `encode_to_string` function instead")] -/// Encode data to a Base64-URL string and directly store into a String instance by concatenating them. It is usually for generating a URL. -#[inline] -pub fn encode_and_push_to_string, S: Into>( +pub fn encode_to_slice<'a, T: ?Sized + AsRef<[u8]>>( input: &T, - output: S, -) -> String { - let mut output = output.into(); - - encode_to_string(input, &mut output); - - output -} + output: &'a mut [u8], +) -> Result<&'a [u8], base64::EncodeSliceError> { + let length = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode_slice(input, output)?; -#[deprecated(since = "1.4.0", note = "Please use the `encode_to_string` function instead")] -/// Encode data to a Base64-URL string and directly store into a mutable String reference by concatenating them. It is usually for generating a URL. -pub fn encode_and_push_to_string_mut>(input: &T, output: &mut String) { - encode_to_string(input, output); + Ok(&output[..length]) } diff --git a/src/escape.rs b/src/escape.rs index 14c8930..e906bee 100644 --- a/src/escape.rs +++ b/src/escape.rs @@ -1,5 +1,5 @@ -use std::borrow::Cow; -use std::str::from_utf8_unchecked; +use alloc::{borrow::Cow, string::String, vec::Vec}; +use core::str::from_utf8_unchecked; /// Escape a Base64 string to a Base64-URL string. The conversion is not concerning with Base64 decoding. You need to make sure the input string is a correct Base64 string by yourself. #[inline] @@ -11,12 +11,12 @@ pub fn escape>(base64: &S) -> Cow { let base64_url = unsafe { String::from_utf8_unchecked(base64_url) }; Cow::from(base64_url) - } + }, Cow::Borrowed(base64_url) => { let base64_url = unsafe { from_utf8_unchecked(base64_url) }; Cow::from(base64_url) - } + }, } } @@ -39,13 +39,13 @@ pub fn escape_u8_slice>(base64: &S) -> Cow<[u8]> { match e { 43 => { break 45; - } + }, 47 => { break 95; - } + }, 61 => { return Cow::from(&base64[..p]); - } + }, _ => (), } @@ -73,13 +73,13 @@ pub fn escape_u8_slice>(base64: &S) -> Cow<[u8]> { start = p + 1; base64_url.push(45); - } + }, 47 => { base64_url.extend_from_slice(&base64[start..p]); start = p + 1; base64_url.push(95); - } + }, 61 => break, _ => (), } @@ -128,7 +128,7 @@ pub fn escape_u8_slice_in_place>(base64: &mut S) -> &[u8 61 => { len = index; break; - } + }, _ => (), } } diff --git a/src/lib.rs b/src/lib.rs index 6dc5e5e..ba21973 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,6 +40,10 @@ assert_eq!("https://example.com/?hash=AQIDBAUGBwgJ", url); ``` */ +#![no_std] + +extern crate alloc; + pub extern crate base64; mod decode; diff --git a/src/unescape.rs b/src/unescape.rs index 5fa9eb4..30405dd 100644 --- a/src/unescape.rs +++ b/src/unescape.rs @@ -1,6 +1,5 @@ -use std::borrow::Cow; -use std::mem::swap; -use std::str::from_utf8_unchecked; +use alloc::{borrow::Cow, string::String, vec::Vec}; +use core::{mem::swap, str::from_utf8_unchecked}; /// Unescape a Base64-URL string to a Base64 string. The conversion is not concerning with Base64 decoding. You need to make sure the input string is a correct Base64-URL string by yourself. #[inline] @@ -12,12 +11,12 @@ pub fn unescape>(base64_url: &S) -> Cow { let base64 = unsafe { String::from_utf8_unchecked(base64) }; Cow::from(base64) - } + }, Cow::Borrowed(base64) => { let base64 = unsafe { from_utf8_unchecked(base64) }; Cow::from(base64) - } + }, } } @@ -49,13 +48,13 @@ pub fn unescape_u8_slice>(base64_url: &S) -> Cow<[u8]> { start = p + 1; base64.push(43); - } + }, 95 => { base64.extend_from_slice(&base64_url[start..p]); start = p + 1; base64.push(47); - } + }, _ => (), } @@ -80,10 +79,10 @@ pub fn unescape_u8_slice>(base64_url: &S) -> Cow<[u8]> { match e { 45 => { break 43; - } + }, 95 => { break 47; - } + }, _ => (), } @@ -111,13 +110,13 @@ pub fn unescape_u8_slice>(base64_url: &S) -> Cow<[u8]> { start = p + 1; base64.push(43); - } + }, 95 => { base64.extend_from_slice(&base64_url[start..p]); start = p + 1; base64.push(47); - } + }, _ => (), } diff --git a/tests/escape.rs b/tests/escape.rs index bdb9ed6..5f1086f 100644 --- a/tests/escape.rs +++ b/tests/escape.rs @@ -1,16 +1,20 @@ +use base64::Engine; use base64_url::base64; #[test] fn escape() { assert_eq!( "aHR0cHM6Ly9tYWdpY2xlbi5vcmc", - base64_url::escape(&base64::encode("https://magiclen.org")) + base64_url::escape( + base64::engine::general_purpose::STANDARD.encode("https://magiclen.org").as_str() + ) ); } #[test] fn escape_in_place() { - let mut base64_string = base64::encode("https://magiclen.org"); + let mut base64_string = + base64::engine::general_purpose::STANDARD.encode("https://magiclen.org"); base64_url::escape_in_place(&mut base64_string); diff --git a/tests/unescape.rs b/tests/unescape.rs index 07e724a..f8bbaae 100644 --- a/tests/unescape.rs +++ b/tests/unescape.rs @@ -1,10 +1,12 @@ +use base64::Engine; use base64_url::base64; #[test] fn unescape() { assert_eq!( b"https://magiclen.org", - base64::decode(base64_url::unescape("aHR0cHM6Ly9tYWdpY2xlbi5vcmc").as_bytes()) + base64::engine::general_purpose::STANDARD + .decode(base64_url::unescape("aHR0cHM6Ly9tYWdpY2xlbi5vcmc").as_bytes()) .unwrap() .as_slice() ); @@ -16,5 +18,8 @@ fn unescape_in_place() { base64_url::unescape_in_place(&mut base64_url_string); - assert_eq!(b"https://magiclen.org", base64::decode(base64_url_string).unwrap().as_slice()); + assert_eq!( + b"https://magiclen.org", + base64::engine::general_purpose::STANDARD.decode(base64_url_string).unwrap().as_slice() + ); }