Repository: alacritty/alacritty
File: alacritty_config_derive/src/de_struct.rs (Rust)

Code before:
    // Iterate over all #[config(...)] attributes.
    for attr in field.attrs.iter().filter(|attr| crate::path_ends_with(&attr.path, "config")) {
        let parsed = match attr.parse_args::<Attr>() {
            Ok(parsed) => parsed,
            Err(_) => continue,
        };

        match parsed.ident.as_str() {
            // Skip deserialization for `#[config(skip)]` fields.
            "skip" => return Ok(()),
            "flatten" => {
                // NOTE: Currently only a single instance of flatten is supported per struct
                // for complexity reasons.
                if !field_streams.flatten.is_empty() {
                    return Err(Error::new(attr.span(), MULTIPLE_FLATTEN_ERROR));
                }

                // Create the tokens to deserialize the flattened struct from the unused fields.
                field_streams.flatten.extend(quote! {
                    let unused = serde_yaml::Value::Mapping(unused);
                    config.#ident = serde::Deserialize::deserialize(unused).unwrap_or_default();
                });
            },
            "deprecated" | "removed" => {
                // Construct deprecation/removal message and append optional attribute override.
                let mut message = format!("Config warning: {} is {}", literal, parsed.ident);
                if let Some(warning) = parsed.param {
                    message = format!("{}; {}", message, warning.value());
                }

                // Append stream to log deprecation/removal warning.
                match_assignment_stream.extend(quote! {
                    log::warn!(target: #LOG_TARGET, #message);
                });
            },
            // Add aliases to match pattern.
            "alias" => {
                if let Some(alias) = parsed.param {
                    literals.push(alias.value());
                }
            },
            _ => (),
        }
    }

    // Create token stream for deserializing "none" string into `Option<T>`.
    if let Type::Path(type_path) = &field.ty {
        if crate::path_ends_with(&type_path.path, "Option") {
            match_assignment_stream = quote! {
                if value.as_str().map_or(false, |s| s.eq_ignore_ascii_case("none")) {
                    config.#ident = None;

Review comment (suggestion):
I don't think "XYZ is removed" is correct. While "is" might be accurate for deprecation, the past tense would need to be used for removal.

Code after:
    // Iterate over all #[config(...)] attributes.
    for attr in field.attrs.iter().filter(|attr| crate::path_ends_with(&attr.path, "config")) {
        let parsed = match attr.parse_args::<Attr>() {
            Ok(parsed) => parsed,
            Err(_) => continue,
        };

        match parsed.ident.as_str() {
            // Skip deserialization for `#[config(skip)]` fields.
            "skip" => return Ok(()),
            "flatten" => {
                // NOTE: Currently only a single instance of flatten is supported per struct
                // for complexity reasons.
                if !field_streams.flatten.is_empty() {
                    return Err(Error::new(attr.span(), MULTIPLE_FLATTEN_ERROR));
                }

                // Create the tokens to deserialize the flattened struct from the unused fields.
                field_streams.flatten.extend(quote! {
                    let unused = serde_yaml::Value::Mapping(unused);
                    config.#ident = serde::Deserialize::deserialize(unused).unwrap_or_default();
                });
            },
            "deprecated" | "removed" => {
                // Construct deprecation/removal message with optional attribute override.
                let mut message = format!("Config warning: {} has been {}", literal, parsed.ident);
                if let Some(warning) = parsed.param {
                    message = format!("{}; {}", message, warning.value());
                }

                // Append stream to log deprecation/removal warning.
                match_assignment_stream.extend(quote! {
                    log::warn!(target: #LOG_TARGET, #message);
                });
            },
            // Add aliases to match pattern.
            "alias" => {
                if let Some(alias) = parsed.param {
                    literals.push(alias.value());
                }
            },
            _ => (),
        }
    }

    // Create token stream for deserializing "none" string into `Option<T>`.
    if let Type::Path(type_path) = &field.ty {
        if crate::path_ends_with(&type_path.path, "Option") {
            match_assignment_stream = quote! {
                if value.as_str().map_or(false, |s| s.eq_ignore_ascii_case("none")) {
                    config.#ident = None;