You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: support.rs
    path of file: ./repos/spot/src/app/models/songs
    
    the code of the file until where you have to start completion: use std::collections::HashMap;
use std::convert::{TryFrom, TryInto};

use crate::app::models::*;

// A range of numbers [a, b], empty range is allowed as well
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Range {
    Empty,
    NotEmpty(u32, u32),
}

impl Range {
    // Create a range [a, b] if b >= a, or an empty range otherwise
    fn of(a: impl TryInto<u32>, b: impl TryInto<u32>) -> Self {
        match (a.try_into(), b.try_into()) {
            (Ok(a), Ok(b)) if b >= a => Self::NotEmpty(a, b),
            _ => Self::Empty,

    
    pub fn remove(&mut self, ids: &[String]) -> ListRangeUpdate {
        let len = self.total_loaded;
        let mut batches = HashMap::<usize, Vec<String>>::default();
        self.iter_ids_from(0)
            .filter(|(_, s)| !ids.contains(s))
            // Removing is expensive, we have to recreate all batches
            .for_each(|(_, next)| {
                Self::batches_add(&mut batches, self.batch_size, next);
            });
        self.last_batch_key = batches.len().saturating_sub(1);
        self.batches = batches;
        let removed = ids.len();
        self.total = self.total.saturating_sub(removed);
        self.total_loaded = self.total_loaded.saturating_sub(removed);
        // Lazy computation of the affected range, basically assume everything has changed
        ListRangeUpdate(0, len as i32, self.total_loaded as i32)
    }