import { describe, expect, test } from 'vitest'
import { isBuild, isServe, page, readFile, serverLogs } from '~utils'

describe.runIf(isBuild)('build', () => {
  test('es', async () => {
    expect(await page.textContent('.es')).toBe('It works')
  })

  test('umd', async () => {
    expect(await page.textContent('.umd')).toBe('It works')
    const code = readFile('dist/my-lib-custom-filename.umd.cjs')
    const noMinifyCode = readFile(
      'dist/nominify/my-lib-custom-filename.umd.cjs',
    )
    const namedCode = readFile('dist/named/my-lib-named.umd.cjs')
    // esbuild helpers are injected inside of the UMD wrapper
    expect(code).toMatch(/^\/\*[^*]*\*\/\s*\(function\(/)
    expect(noMinifyCode).toMatch(
      /^\/\*[^*]*\*\/\s*\(function\(global.+?function\smyLib\(/s,
    )
    expect(namedCode).toMatch(/^\(function\(/)
  })

  test('iife', async () => {
    expect(await page.textContent('.iife')).toBe('It works')
    const code = readFile('dist/my-lib-custom-filename.iife.js')
    const noMinifyCode = readFile(
      'dist/nominify/my-lib-custom-filename.iife.js',
    )
    const namedCode = readFile('dist/named/my-lib-named.iife.js')
    // esbuild helpers are injected inside of the IIFE wrapper
    expect(code).toMatch(/^\/\*[^*]*\*\/\s*var MyLib=\(function\(\)\{\s*/)
    expect(noMinifyCode).toMatch(
      /^\/\*[^*]*\*\/\s*var MyLib\s*=\s*\(function\(\)\s*\{\s*/,
    )
    expect(namedCode).toMatch(/^var MyLibNamed=\(function\([^()]+\)\{\s*/)
  })

  test('restrisct-helpers-injection', async () => {
    const code = readFile(
      'dist/helpers-injection/my-lib-custom-filename.iife.js',
    )
    expect(code).toMatch(
      `\\"use strict\\"; return (" + expressionSyntax + ").constructor;"`,
    )
  })

  test('Library mode does not include `preload`', async () => {
    await expect
      .poll(() => page.textContent('.dynamic-import-message'))
      .toMatch('hello vite')
    const code = readFile('dist/lib/dynamic-import-message.es.mjs')
    expect(code).not.toMatch('__vitePreload')

    // Test that library chunks are hashed
    expect(code).toMatch(/await import\(['"`]\.\/message-[-\w]{8}.js['"`]\)/)
  })

  test('Library mode does not have any reference to pure CSS chunks', async () => {
    const code = readFile('dist/lib/dynamic-import-message.es.mjs')

    // Does not import pure CSS chunks and replaced by `Promise.resolve({})` instead
    expect(code).not.toMatch(
      /await import\(['"`]\.\/dynamic-[-\w]{8}.js['"`]\)/,
    )
    expect(code).toMatch(/await Promise.resolve\(\{.*\}\)/)
  })

  test('pure annotations are kept for es output', () => {
    const es = readFile('dist/my-lib-custom-filename.js')
    expect(es).toMatch(/[@#]__PURE__/)
  })

  test('pure annotations are removed for non-es output', () => {
    const es = readFile('dist/my-lib-custom-filename.iife.js')
    expect(es).not.toMatch(/[@#]__PURE__/)
  })

  test('@import hoist', async () => {
    serverLogs.forEach((log) => {
      // no warning from esbuild css minifier
      expect(log).not.toMatch('All "@import" rules must come first')
    })
  })

  test('preserve process.env', () => {
    const es = readFile('dist/my-lib-custom-filename.js')
    const iife = readFile('dist/my-lib-custom-filename.iife.js')
    const umd = readFile('dist/my-lib-custom-filename.umd.cjs')
    expect(es).toMatch('process.env.NODE_ENV')
    expect(iife).toMatch('process.env.NODE_ENV')
    expect(umd).toMatch('process.env.NODE_ENV')
  })

  test('debugger statements are removed by terser for es', () => {
    const terserEs = readFile('dist/terser/my-lib-custom-filename.js')
    expect(terserEs).not.toMatch('debugger')
  })

  test('pure annotations are not removed by terser for es', () => {
    const terserEs = readFile('dist/terser/my-lib-custom-filename.js')
    expect(terserEs).toMatch(/[@#]__PURE__/)
  })

  test('pure annotations are removed by terser for non-es output', () => {
    const terserIife = readFile('dist/terser/my-lib-custom-filename.iife.js')
    expect(terserIife).not.toMatch(/[@#]__PURE__/)
  })

  test('single entry with css', () => {
    const css = readFile('dist/css-single-entry/test-my-lib.css')
    const js = readFile('dist/css-single-entry/test-my-lib.js')
    const umd = readFile('dist/css-single-entry/test-my-lib.umd.cjs')
    expect(css).toMatch('entry-1.css')
    expect(js).toMatch('css-entry-1')
    expect(umd).toContain('css-entry-1')
  })

  test('multi entry with css', () => {
    const css = readFile('dist/css-multi-entry/test-my-lib.css')
    const js1 = readFile('dist/css-multi-entry/css-entry-1.js')
    const js2 = readFile('dist/css-multi-entry/css-entry-2.js')
    const cjs1 = readFile('dist/css-multi-entry/css-entry-1.cjs')
    const cjs2 = readFile('dist/css-multi-entry/css-entry-2.cjs')
    expect(css).toMatch('entry-1.css')
    expect(css).toMatch('entry-2.css')
    expect(js1).toMatch('css-entry-1')
    expect(js2).toMatch('css-entry-2')
    expect(cjs1).toContain('css-entry-1')
    expect(cjs2).toContain('css-entry-2')
  })

  test('multi entry with css and code split', () => {
    const css1 = readFile('dist/css-code-split/css-entry-1.css')
    const css2 = readFile('dist/css-code-split/css-entry-2.css')
    const js1 = readFile('dist/css-code-split/css-entry-1.js')
    const js2 = readFile('dist/css-code-split/css-entry-2.js')
    const cjs1 = readFile('dist/css-code-split/css-entry-1.cjs')
    const cjs2 = readFile('dist/css-code-split/css-entry-2.cjs')
    expect(css1).toMatch('entry-1.css')
    expect(css2).toMatch('entry-2.css')
    expect(js1).toMatch('css-entry-1')
    expect(js2).toMatch('css-entry-2')
    expect(cjs1).toContain('css-entry-1')
    expect(cjs2).toContain('css-entry-2')
  })
})

test.runIf(isServe)('dev', async () => {
  await expect.poll(() => page.textContent('.demo')).toBe('It works')
})


use crate::de::identifier;
use crate::de::{
    deserialize_seq, expr_is_missing, field_i, has_flatten, wrap_deserialize_field_with,
    FieldWithAliases, Parameters, StructForm,
};
#[cfg(feature = "deserialize_in_place")]
use crate::de::{deserialize_seq_in_place, place_lifetime};
use crate::fragment::{Expr, Fragment, Match, Stmts};
use crate::internals::ast::Field;
use crate::internals::attr;
use crate::private;
use proc_macro2::TokenStream;
use quote::{quote, quote_spanned};
use syn::spanned::Spanned;

/// Generates `Deserialize::deserialize` body for a `struct Struct {...}`
pub(super) fn deserialize(
    params: &Parameters,
    fields: &[Field],
    cattrs: &attr::Container,
    form: StructForm,
) -> Fragment {
    let this_type = &params.this_type;
    let this_value = &params.this_value;
    let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
        params.generics_with_de_lifetime();
    let delife = params.borrowed.de_lifetime();

    // If there are getters (implying private fields), construct the local type
    // and use an `Into` conversion to get the remote type. If there are no
    // getters then construct the target type directly.
    let construct = if params.has_getter {
        let local = &params.local;
        quote!(#local)
    } else {
        quote!(#this_value)
    };

    let type_path = match form {
        StructForm::Struct => construct,
        StructForm::ExternallyTagged(variant_ident)
        | StructForm::InternallyTagged(variant_ident)
        | StructForm::Untagged(variant_ident) => quote!(#construct::#variant_ident),
    };
    let expecting = match form {
        StructForm::Struct => format!("struct {}", params.type_name()),
        StructForm::ExternallyTagged(variant_ident)
        | StructForm::InternallyTagged(variant_ident)
        | StructForm::Untagged(variant_ident) => {
            format!("struct variant {}::{}", params.type_name(), variant_ident)
        }
    };
    let expecting = cattrs.expecting().unwrap_or(&expecting);

    let deserialized_fields: Vec<_> = fields
        .iter()
        .enumerate()
        // Skip fields that shouldn't be deserialized or that were flattened,
        // so they don't appear in the storage in their literal form
        .filter(|&(_, field)| !field.attrs.skip_deserializing() && !field.attrs.flatten())
        .map(|(i, field)| FieldWithAliases {
            ident: field_i(i),
            aliases: field.attrs.aliases(),
        })
        .collect();

    let has_flatten = has_flatten(fields);
    let field_visitor = deserialize_field_identifier(&deserialized_fields, cattrs, has_flatten);

    // untagged struct variants do not get a visit_seq method. The same applies to
    // structs that only have a map representation.
    let visit_seq = match form {
        StructForm::Untagged(_) => None,
        _ if has_flatten => None,
        _ => {
            let mut_seq = if deserialized_fields.is_empty() {
                quote!(_)
            } else {
                quote!(mut __seq)
            };

            let visit_seq = Stmts(deserialize_seq(
                &type_path, params, fields, true, cattrs, expecting,
            ));

            Some(quote! {
                #[inline]
                fn visit_seq<__A>(self, #mut_seq: __A) -> _serde::#private::Result<Self::Value, __A::Error>
                where
                    __A: _serde::de::SeqAccess<#delife>,
                {
                    #visit_seq
                }
            })
        }
    };
    let visit_map = Stmts(deserialize_map(
        &type_path,
        params,
        fields,
        cattrs,
        has_flatten,
    ));

    let visitor_seed = match form {
        StructForm::ExternallyTagged(..) if has_flatten => Some(quote! {
            #[automatically_derived]
            impl #de_impl_generics _serde::de::DeserializeSeed<#delife> for __Visitor #de_ty_generics #where_clause {
                type Value = #this_type #ty_generics;

                fn deserialize<__D>(self, __deserializer: __D) -> _serde::#private::Result<Self::Value, __D::Error>
                where
                    __D: _serde::Deserializer<#delife>,
                {
                    _serde::Deserializer::deserialize_map(__deserializer, self)
                }
            }
        }),
        _ => None,
    };

    let fields_stmt = if has_flatten {
        None
    } else {
        let field_names = deserialized_fields.iter().flat_map(|field| field.aliases);

        Some(quote! {
            #[doc(hidden)]
            const FIELDS: &'static [&'static str] = &[ #(#field_names),* ];
        })
    };

    let visitor_expr = quote! {
        __Visitor {
            marker: _serde::#private::PhantomData::<#this_type #ty_generics>,
            lifetime: _serde::#private::PhantomData,
        }
    };
    let dispatch = match form {
        StructForm::Struct if has_flatten => quote! {
            _serde::Deserializer::deserialize_map(__deserializer, #visitor_expr)
        },
        StructForm::Struct => {
            let type_name = cattrs.name().deserialize_name();
            quote! {
                _serde::Deserializer::deserialize_struct(__deserializer, #type_name, FIELDS, #visitor_expr)
            }
        }
        StructForm::ExternallyTagged(_) if has_flatten => quote! {
            _serde::de::VariantAccess::newtype_variant_seed(__variant, #visitor_expr)
        },
        StructForm::ExternallyTagged(_) => quote! {
            _serde::de::VariantAccess::struct_variant(__variant, FIELDS, #visitor_expr)
        },
        StructForm::InternallyTagged(_) => quote! {
            _serde::Deserializer::deserialize_any(__deserializer, #visitor_expr)
        },
        StructForm::Untagged(_) => quote! {
            _serde::Deserializer::deserialize_any(__deserializer, #visitor_expr)
        },
    };

    quote_block! {
        #field_visitor

        #[doc(hidden)]
        struct __Visitor #de_impl_generics #where_clause {
            marker: _serde::#private::PhantomData<#this_type #ty_generics>,
            lifetime: _serde::#private::PhantomData<&#delife ()>,
        }

        #[automatically_derived]
        impl #de_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_ty_generics #where_clause {
            type Value = #this_type #ty_generics;

            fn expecting(&self, __formatter: &mut _serde::#private::Formatter) -> _serde::#private::fmt::Result {
                _serde::#private::Formatter::write_str(__formatter, #expecting)
            }

            #visit_seq

            #[inline]
            fn visit_map<__A>(self, mut __map: __A) -> _serde::#private::Result<Self::Value, __A::Error>
            where
                __A: _serde::de::MapAccess<#delife>,
            {
                #visit_map
            }
        }

        #visitor_seed

        #fields_stmt

        #dispatch
    }
}

fn deserialize_map(
    struct_path: &TokenStream,
    params: &Parameters,
    fields: &[Field],
    cattrs: &attr::Container,
    has_flatten: bool,
) -> Fragment {
    // Create the field names for the fields.
    let fields_names: Vec<_> = fields
        .iter()
        .enumerate()
        .map(|(i, field)| (field, field_i(i)))
        .collect();

    // Declare each field that will be deserialized.
    let let_values = fields_names
        .iter()
        .filter(|&&(field, _)| !field.attrs.skip_deserializing() && !field.attrs.flatten())
        .map(|(field, name)| {
            let field_ty = field.ty;
            quote! {
                let mut #name: _serde::#private::Option<#field_ty> = _serde::#private::None;
            }
        });

    // Collect contents for flatten fields into a buffer
    let let_collect = if has_flatten {
        Some(quote! {
            let mut __collect = _serde::#private::Vec::<_serde::#private::Option<(
                _serde::#private::de::Content,
                _serde::#private::de::Content
            )>>::new();
        })
    } else {
        None
    };

    // Match arms to extract a value for a field.
    let value_arms = fields_names
        .iter()
        .filter(|&&(field, _)| !field.attrs.skip_deserializing() && !field.attrs.flatten())
        .map(|(field, name)| {
            let deser_name = field.attrs.name().deserialize_name();

            let visit = match field.attrs.deserialize_with() {
                None => {
                    let field_ty = field.ty;
                    let span = field.original.span();
                    let func =
                        quote_spanned!(span=> _serde::de::MapAccess::next_value::<#field_ty>);
                    quote! {
                        #func(&mut __map)?
                    }
                }
                Some(path) => {
                    let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path);
                    quote!({
                        #wrapper
                        match _serde::de::MapAccess::next_value::<#wrapper_ty>(&mut __map) {
                            _serde::#private::Ok(__wrapper) => __wrapper.value,
                            _serde::#private::Err(__err) => {
                                return _serde::#private::Err(__err);
                            }
                        }
                    })
                }
            };
            quote! {
                __Field::#name => {
                    if _serde::#private::Option::is_some(&#name) {
                        return _serde::#private::Err(<__A::Error as _serde::de::Error>::duplicate_field(#deser_name));
                    }
                    #name = _serde::#private::Some(#visit);
                }
            }
        });

    // Visit ignored values to consume them
    let ignored_arm = if has_flatten {
        Some(quote! {
            __Field::__other(__name) => {
                __collect.push(_serde::#private::Some((
                    __name,
                    _serde::de::MapAccess::next_value_seed(&mut __map, _serde::#private::de::ContentVisitor::new())?)));
            }
        })
    } else if cattrs.deny_unknown_fields() {
        None
    } else {
        Some(quote! {
            _ => { let _ = _serde::de::MapAccess::next_value::<_serde::de::IgnoredAny>(&mut __map)?; }
        })
    };

    let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing());
    let match_keys = if cattrs.deny_unknown_fields() && all_skipped {
        quote! {
            // FIXME: Once feature(exhaustive_patterns) is stable:
            // let _serde::#private::None::<__Field> = _serde::de::MapAccess::next_key(&mut __map)?;
            _serde::#private::Option::map(
                _serde::de::MapAccess::next_key::<__Field>(&mut __map)?,
                |__impossible| match __impossible {});
        }
    } else {
        quote! {
            while let _serde::#private::Some(__key) = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? {
                match __key {
                    #(#value_arms)*
                    #ignored_arm
                }
            }
        }
    };

    let extract_values = fields_names
        .iter()
        .filter(|&&(field, _)| !field.attrs.skip_deserializing() && !field.attrs.flatten())
        .map(|(field, name)| {
            let missing_expr = Match(expr_is_missing(field, cattrs));

            quote! {
                let #name = match #name {
                    _serde::#private::Some(#name) => #name,
                    _serde::#private::None => #missing_expr
                };
            }
        });

    let extract_collected = fields_names
        .iter()
        .filter(|&&(field, _)| field.attrs.flatten() && !field.attrs.skip_deserializing())
        .map(|(field, name)| {
            let field_ty = field.ty;
            let func = match field.attrs.deserialize_with() {
                None => {
                    let span = field.original.span();
                    quote_spanned!(span=> _serde::de::Deserialize::deserialize)
                }
                Some(path) => quote!(#path),
            };
            quote! {
                let #name: #field_ty = #func(
                    _serde::#private::de::FlatMapDeserializer(
                        &mut __collect,
                        _serde::#private::PhantomData))?;
            }
        });

    let collected_deny_unknown_fields = if has_flatten && cattrs.deny_unknown_fields() {
        Some(quote! {
            if let _serde::#private::Some(_serde::#private::Some((__key, _))) =
                __collect.into_iter().filter(_serde::#private::Option::is_some).next()
            {
                if let _serde::#private::Some(__key) = _serde::#private::de::content_as_str(&__key) {
                    return _serde::#private::Err(
                        _serde::de::Error::custom(format_args!("unknown field `{}`", &__key)));
                } else {
                    return _serde::#private::Err(
                        _serde::de::Error::custom(format_args!("unexpected map key")));
                }
            }
        })
    } else {
        None
    };

    let result = fields_names.iter().map(|(field, name)| {
        let member = &field.member;
        if field.attrs.skip_deserializing() {
            let value = Expr(expr_is_missing(field, cattrs));
            quote!(#member: #value)
        } else {
            quote!(#member: #name)
        }
    });

    let let_default = match cattrs.default() {
        attr::Default::Default => Some(quote!(
            let __default: Self::Value = _serde::#private::Default::default();
        )),
        // If #path returns wrong type, error will be reported here (^^^^^).
        // We attach span of the path to the function so it will be reported
        // on the #[serde(default = "...")]
        //                          ^^^^^
        attr::Default::Path(path) => Some(quote_spanned!(path.span()=>
            let __default: Self::Value = #path();
        )),
        attr::Default::None => {
            // We don't need the default value, to prevent an unused variable warning
            // we'll leave the line empty.
            None
        }
    };

    let mut result = quote!(#struct_path { #(#result),* });
    if params.has_getter {
        let this_type = &params.this_type;
        let (_, ty_generics, _) = params.generics.split_for_impl();
        result = quote! {
            _serde::#private::Into::<#this_type #ty_generics>::into(#result)
        };
    }

    quote_block! {
        #(#let_values)*

        #let_collect

        #match_keys

        #let_default

        #(#extract_values)*

        #(#extract_collected)*

        #collected_deny_unknown_fields

        _serde::#private::Ok(#result)
    }
}

/// Generates `Deserialize::deserialize_in_place` body for a `struct Struct {...}`
#[cfg(feature = "deserialize_in_place")]
pub(super) fn deserialize_in_place(
    params: &Parameters,
    fields: &[Field],
    cattrs: &attr::Container,
) -> Option<Fragment> {
    // for now we do not support in_place deserialization for structs that
    // are represented as map.
    if has_flatten(fields) {
        return None;
    }

    let this_type = &params.this_type;
    let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
        params.generics_with_de_lifetime();
    let delife = params.borrowed.de_lifetime();

    let expecting = format!("struct {}", params.type_name());
    let expecting = cattrs.expecting().unwrap_or(&expecting);

    let deserialized_fields: Vec<_> = fields
        .iter()
        .enumerate()
        .filter(|&(_, field)| !field.attrs.skip_deserializing())
        .map(|(i, field)| FieldWithAliases {
            ident: field_i(i),
            aliases: field.attrs.aliases(),
        })
        .collect();

    let field_visitor = deserialize_field_identifier(&deserialized_fields, cattrs, false);

    let mut_seq = if deserialized_fields.is_empty() {
        quote!(_)
    } else {
        quote!(mut __seq)
    };
    let visit_seq = Stmts(deserialize_seq_in_place(params, fields, cattrs, expecting));
    let visit_map = Stmts(deserialize_map_in_place(params, fields, cattrs));
    let field_names = deserialized_fields.iter().flat_map(|field| field.aliases);
    let type_name = cattrs.name().deserialize_name();

    let in_place_impl_generics = de_impl_generics.in_place();
    let in_place_ty_generics = de_ty_generics.in_place();
    let place_life = place_lifetime();

    Some(quote_block! {
        #field_visitor

        #[doc(hidden)]
        struct __Visitor #in_place_impl_generics #where_clause {
            place: &#place_life mut #this_type #ty_generics,
            lifetime: _serde::#private::PhantomData<&#delife ()>,
        }

        #[automatically_derived]
        impl #in_place_impl_generics _serde::de::Visitor<#delife> for __Visitor #in_place_ty_generics #where_clause {
            type Value = ();

            fn expecting(&self, __formatter: &mut _serde::#private::Formatter) -> _serde::#private::fmt::Result {
                _serde::#private::Formatter::write_str(__formatter, #expecting)
            }

            #[inline]
            fn visit_seq<__A>(self, #mut_seq: __A) -> _serde::#private::Result<Self::Value, __A::Error>
            where
                __A: _serde::de::SeqAccess<#delife>,
            {
                #visit_seq
            }

            #[inline]
            fn visit_map<__A>(self, mut __map: __A) -> _serde::#private::Result<Self::Value, __A::Error>
            where
                __A: _serde::de::MapAccess<#delife>,
            {
                #visit_map
            }
        }

        #[doc(hidden)]
        const FIELDS: &'static [&'static str] = &[ #(#field_names),* ];

        _serde::Deserializer::deserialize_struct(__deserializer, #type_name, FIELDS, __Visitor {
            place: __place,
            lifetime: _serde::#private::PhantomData,
        })
    })
}

#[cfg(feature = "deserialize_in_place")]
fn deserialize_map_in_place(
    params: &Parameters,
    fields: &[Field],
    cattrs: &attr::Container,
) -> Fragment {
    assert!(
        !has_flatten(fields),
        "inplace deserialization of maps does not support flatten fields"
    );

    // Create the field names for the fields.
    let fields_names: Vec<_> = fields
        .iter()
        .enumerate()
        .map(|(i, field)| (field, field_i(i)))
        .collect();

    // For deserialize_in_place, declare booleans for each field that will be
    // deserialized.
    let let_flags = fields_names
        .iter()
        .filter(|&&(field, _)| !field.attrs.skip_deserializing())
        .map(|(_, name)| {
            quote! {
                let mut #name: bool = false;
            }
        });

    // Match arms to extract a value for a field.
    let value_arms_from = fields_names
        .iter()
        .filter(|&&(field, _)| !field.attrs.skip_deserializing())
        .map(|(field, name)| {
            let deser_name = field.attrs.name().deserialize_name();
            let member = &field.member;

            let visit = match field.attrs.deserialize_with() {
                None => {
                    quote! {
                        _serde::de::MapAccess::next_value_seed(&mut __map, _serde::#private::de::InPlaceSeed(&mut self.place.#member))?
                    }
                }
                Some(path) => {
                    let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path);
                    quote!({
                        #wrapper
                        self.place.#member = match _serde::de::MapAccess::next_value::<#wrapper_ty>(&mut __map) {
                            _serde::#private::Ok(__wrapper) => __wrapper.value,
                            _serde::#private::Err(__err) => {
                                return _serde::#private::Err(__err);
                            }
                        };
                    })
                }
            };
            quote! {
                __Field::#name => {
                    if #name {
                        return _serde::#private::Err(<__A::Error as _serde::de::Error>::duplicate_field(#deser_name));
                    }
                    #visit;
                    #name = true;
                }
            }
        });

    // Visit ignored values to consume them
    let ignored_arm = if cattrs.deny_unknown_fields() {
        None
    } else {
        Some(quote! {
            _ => { let _ = _serde::de::MapAccess::next_value::<_serde::de::IgnoredAny>(&mut __map)?; }
        })
    };

    let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing());

    let match_keys = if cattrs.deny_unknown_fields() && all_skipped {
        quote! {
            // FIXME: Once feature(exhaustive_patterns) is stable:
            // let _serde::#private::None::<__Field> = _serde::de::MapAccess::next_key(&mut __map)?;
            _serde::#private::Option::map(
                _serde::de::MapAccess::next_key::<__Field>(&mut __map)?,
                |__impossible| match __impossible {});
        }
    } else {
        quote! {
            while let _serde::#private::Some(__key) = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? {
                match __key {
                    #(#value_arms_from)*
                    #ignored_arm
                }
            }
        }
    };

    let check_flags = fields_names
        .iter()
        .filter(|&&(field, _)| !field.attrs.skip_deserializing())
        .map(|(field, name)| {
            let missing_expr = expr_is_missing(field, cattrs);
            // If missing_expr unconditionally returns an error, don't try
            // to assign its value to self.place.
            if field.attrs.default().is_none()
                && cattrs.default().is_none()
                && field.attrs.deserialize_with().is_some()
            {
                let missing_expr = Stmts(missing_expr);
                quote! {
                    if !#name {
                        #missing_expr;
                    }
                }
            } else {
                let member = &field.member;
                let missing_expr = Expr(missing_expr);
                quote! {
                    if !#name {
                        self.place.#member = #missing_expr;
                    };
                }
            }
        });

    let this_type = &params.this_type;
    let (_, ty_generics, _) = params.generics.split_for_impl();

    let let_default = match cattrs.default() {
        attr::Default::Default => Some(quote!(
            let __default: #this_type #ty_generics = _serde::#private::Default::default();
        )),
        // If #path returns wrong type, error will be reported here (^^^^^).
        // We attach span of the path to the function so it will be reported
        // on the #[serde(default = "...")]
        //                          ^^^^^
        attr::Default::Path(path) => Some(quote_spanned!(path.span()=>
            let __default: #this_type #ty_generics = #path();
        )),
        attr::Default::None => {
            // We don't need the default value, to prevent an unused variable warning
            // we'll leave the line empty.
            None
        }
    };

    quote_block! {
        #(#let_flags)*

        #match_keys

        #let_default

        #(#check_flags)*

        _serde::#private::Ok(())
    }
}

/// Generates enum and its `Deserialize` implementation that represents each
/// non-skipped field of the struct
fn deserialize_field_identifier(
    deserialized_fields: &[FieldWithAliases],
    cattrs: &attr::Container,
    has_flatten: bool,
) -> Stmts {
    let (ignore_variant, fallthrough) = if has_flatten {
        let ignore_variant = quote!(__other(_serde::#private::de::Content<'de>),);
        let fallthrough = quote!(_serde::#private::Ok(__Field::__other(__value)));
        (Some(ignore_variant), Some(fallthrough))
    } else if cattrs.deny_unknown_fields() {
        (None, None)
    } else {
        let ignore_variant = quote!(__ignore,);
        let fallthrough = quote!(_serde::#private::Ok(__Field::__ignore));
        (Some(ignore_variant), Some(fallthrough))
    };

    Stmts(identifier::deserialize_generated(
        deserialized_fields,
        has_flatten,
        false,
        ignore_variant,
        fallthrough,
    ))
}


# <small>nlohmann::basic_json::</small>crend

```cpp
const_reverse_iterator crend() const noexcept;
```

Returns an iterator to the reverse-end; that is, one before the first element. This element acts as a placeholder,
attempting to access it results in undefined behavior.

![Illustration from cppreference.com](../../images/range-rbegin-rend.svg)

## Return value

reverse iterator to the element following the last element

## Exception safety

No-throw guarantee: this member function never throws exceptions.

## Complexity

Constant.

## Examples

??? example

    The following code shows an example for `crend()`.

    ```cpp
    --8<-- "examples/crend.cpp"
    ```

    Output:

    ```json
    --8<-- "examples/crend.output"
    ```

## Version history

- Added in version 1.0.0.


import path from 'node:path'
import { createServer } from 'vite'
import { expect, test } from 'vitest'
import { getBgColor, getColor, isServe, page, ports } from '~utils'

// Regression test for https://github.com/vitejs/vite/issues/4000
test.runIf(isServe)('postcss plugins in different dir', async () => {
  const port = ports['css/postcss-plugins-different-dir']
  const server = await createServer({
    root: path.join(import.meta.dirname, '..', '..', '..', 'tailwind'),
    logLevel: 'silent',
    server: {
      port,
      strictPort: true,
    },
    build: {
      // skip transpilation during tests to make it faster
      target: 'esnext',
    },
  })
  await server.listen()
  try {
    await page.goto(`http://localhost:${port}`)
    const tailwindStyle = page.locator('#tailwind-style')
    expect(await getBgColor(tailwindStyle)).toBe('oklch(0.936 0.032 17.717)')
    expect(await getColor(tailwindStyle)).toBe('rgb(136, 136, 136)')
  } finally {
    await server.close()
  }
})


import { beforeEach, describe, expect, test } from 'vitest'
import { findAssetFile, isBuild, startDefaultServe } from '~utils'

beforeEach(async () => {
  await startDefaultServe()
})

for (let i = 0; i < 5; i++) {
  describe.runIf(isBuild)('css-codesplit build', () => {
    test('should be consistent with same content', () => {
      expect(findAssetFile(/style-.+\.css/)).toBeUndefined()
      expect(findAssetFile(/style2-.+\.css/)).toMatch('h2{color:#00f}')
    })
  })
}


import type { PartialResolvedId } from 'rolldown'
import aliasPlugin from '@rollup/plugin-alias'
import type { ResolvedConfig } from './config'
import type { EnvironmentPluginContainer } from './server/pluginContainer'
import { createEnvironmentPluginContainer } from './server/pluginContainer'
import { oxcResolvePlugin } from './plugins/resolve'
import type { InternalResolveOptions } from './plugins/resolve'
import type { Environment } from './environment'
import type { PartialEnvironment } from './baseEnvironment'

export type ResolveIdFn = (
  environment: PartialEnvironment,
  id: string,
  importer?: string,
  aliasOnly?: boolean,
) => Promise<string | undefined>

/**
 * Some projects like Astro were overriding config.createResolver to add a custom
 * alias plugin. For the client and ssr environments, we root through it to avoid
 * breaking changes for now.
 */
export function createBackCompatIdResolver(
  config: ResolvedConfig,
  options?: Partial<InternalResolveOptions>,
): ResolveIdFn {
  const compatResolve = config.createResolver(options)
  let resolve: ResolveIdFn
  return async (environment, id, importer, aliasOnly) => {
    if (environment.name === 'client' || environment.name === 'ssr') {
      return compatResolve(id, importer, aliasOnly, environment.name === 'ssr')
    }
    resolve ??= createIdResolver(config, options)
    return resolve(environment, id, importer, aliasOnly)
  }
}

/**
 * Create an internal resolver to be used in special scenarios, e.g.
 * optimizer and handling css @imports
 */
export function createIdResolver(
  config: ResolvedConfig,
  options?: Partial<InternalResolveOptions>,
): ResolveIdFn {
  const scan = options?.scan

  const pluginContainerMap = new Map<
    PartialEnvironment,
    EnvironmentPluginContainer
  >()
  async function resolve(
    environment: PartialEnvironment,
    id: string,
    importer?: string,
  ): Promise<PartialResolvedId | null> {
    let pluginContainer = pluginContainerMap.get(environment)
    if (!pluginContainer) {
      pluginContainer = await createEnvironmentPluginContainer(
        environment as Environment,
        [
          // @ts-expect-error  the aliasPlugin uses rollup types
          aliasPlugin({ entries: environment.config.resolve.alias }),
          ...oxcResolvePlugin(
            {
              root: config.root,
              isProduction: config.isProduction,
              isBuild: config.command === 'build',
              asSrc: true,
              preferRelative: false,
              tryIndex: true,
              ...options,
              // Ignore sideEffects and other computations as we only need the id
              idOnly: true,
            },
            environment.config,
            true,
          ),
        ],
        undefined,
        false,
      )
      pluginContainerMap.set(environment, pluginContainer)
    }
    return await pluginContainer.resolveId(id, importer, { scan })
  }

  const aliasOnlyPluginContainerMap = new Map<
    PartialEnvironment,
    EnvironmentPluginContainer
  >()
  async function resolveAlias(
    environment: PartialEnvironment,
    id: string,
    importer?: string,
  ): Promise<PartialResolvedId | null> {
    let pluginContainer = aliasOnlyPluginContainerMap.get(environment)
    if (!pluginContainer) {
      pluginContainer = await createEnvironmentPluginContainer(
        environment as Environment,
        // @ts-expect-error  the aliasPlugin uses rollup types
        [aliasPlugin({ entries: environment.config.resolve.alias })],
        undefined,
        false,
      )
      aliasOnlyPluginContainerMap.set(environment, pluginContainer)
    }
    return await pluginContainer.resolveId(id, importer, { scan })
  }

  return async (environment, id, importer, aliasOnly) => {
    const resolveFn = aliasOnly ? resolveAlias : resolve
    // aliasPlugin and resolvePlugin are implemented to function with a Environment only,
    // we cast it as PluginEnvironment to be able to use the pluginContainer
    const resolved = await resolveFn(environment, id, importer)
    return resolved?.id
  }
}


import pytest

from flaskr.db import get_db

def test_index(client, auth):
    response = client.get("/")
    assert b"Log In" in response.data
    assert b"Register" in response.data

    auth.login()
    response = client.get("/")
    assert b"test title" in response.data
    assert b"by test on 2018-01-01" in response.data
    assert b"test\nbody" in response.data
    assert b'href="/1/update"' in response.data

@pytest.mark.parametrize("path", ("/create", "/1/update", "/1/delete"))
def test_login_required(client, path):
    response = client.post(path)
    assert response.headers["Location"] == "/auth/login"

def test_author_required(app, client, auth):
    # change the post author to another user
    with app.app_context():
        db = get_db()
        db.execute("UPDATE post SET author_id = 2 WHERE id = 1")
        db.commit()

    auth.login()
    # current user can't modify other user's post
    assert client.post("/1/update").status_code == 403
    assert client.post("/1/delete").status_code == 403
    # current user doesn't see edit link
    assert b'href="/1/update"' not in client.get("/").data

@pytest.mark.parametrize("path", ("/2/update", "/2/delete"))
def test_exists_required(client, auth, path):
    auth.login()
    assert client.post(path).status_code == 404

def test_create(client, auth, app):
    auth.login()
    assert client.get("/create").status_code == 200
    client.post("/create", data={"title": "created", "body": ""})

    with app.app_context():
        db = get_db()
        count = db.execute("SELECT COUNT(id) FROM post").fetchone()[0]
        assert count == 2

def test_update(client, auth, app):
    auth.login()
    assert client.get("/1/update").status_code == 200
    client.post("/1/update", data={"title": "updated", "body": ""})

    with app.app_context():
        db = get_db()
        post = db.execute("SELECT * FROM post WHERE id = 1").fetchone()
        assert post["title"] == "updated"

@pytest.mark.parametrize("path", ("/create", "/1/update"))
def test_create_update_validate(client, auth, path):
    auth.login()
    response = client.post(path, data={"title": "", "body": ""})
    assert b"Title is required." in response.data

def test_delete(client, auth, app):
    auth.login()
    response = client.post("/1/delete")
    assert response.headers["Location"] == "/"

    with app.app_context():
        db = get_db()
        post = db.execute("SELECT * FROM post WHERE id = 1").fetchone()
        assert post is None