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: globwatcher.rs
    path of file: ./repos/turbo/crates/turborepo-filewatch/src
    
    the code of the file until where you have to start completion: use std::{
    collections::{HashMap, HashSet},
    fmt::Display,
    future::IntoFuture,
    str::FromStr,
    time::Duration,
};

use notify::Event;
use thiserror::Error;
use tokio::sync::{broadcast, mpsc, oneshot};
use tracing::{debug, warn};
use turbopath::{AbsoluteSystemPathBuf, RelativeUnixPath};
use wax::{Any, Glob, Program};

use crate::{
    cookies::{CookieError, CookieWatcher, CookieWriter, CookiedRequest},
    NotifyError, OptionalWatch,
};

type Hash = String;

pub struct GlobSet {
    include: HashMap<String, wax::Glob<'static>>,
    exclude: Any<'static>,
    exclude_raw: Vec<String>,
}

impl std::fmt::Debug for GlobSet {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("GlobSet")
            .field("include", &self.include.keys())
            .field("exclude", &self.exclude_raw)
            .finish()
    }
}

#[derive(Debug, Error)]
pub struct GlobError {
    // Boxed to minimize error size
    underlying: Box<wax::BuildError>,
    raw_glob: String,
}

impl Display for GlobError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}: {}", self.underlying, self.raw_glob)
    }
}

fn compile_glob(raw: &str) -> Result<Glob<'static>, GlobError> {
    Glob::from_str(raw)
        .map(|g| g.to_owned())
        .map_err(|e| GlobError {
            underlying: Box::new(e),
            raw_glob: raw.to_owned(),
        })
}

impl GlobSet {
    pub fn from_raw(
        raw_includes: Vec<String>,
        raw_excludes: Vec<String>,
    ) -> Result<Self, GlobError> {
        let include = raw_includes
            .into_iter()
            .map(|raw_glob| {
                let glob = compile_glob(&raw_glob)?;
                Ok((raw_glob, glob))
            })
            .collect::<Result<HashMap<_, _>, GlobError>>()?;
        let excludes 
    
    async fn test_track_multiple_hashes() {
        let timeout = Duration::from_secs(2);
        let (repo_root, _tmp_dir) = temp_dir();
        setup(&repo_root);
        let cookie_dir = repo_root.join_component(".git");

        let watcher = FileSystemWatcher::new_with_default_cookie_dir(&repo_root).unwrap();
        let recv = watcher.watch();
        let cookie_writer = CookieWriter::new(&cookie_dir, Duration::from_secs(2), recv.clone());

        let glob_watcher = GlobWatcher::new(repo_root.clone(), cookie_writer, recv);

        let raw_includes = &["my-pkg/dist/**", "my-pkg/.next/**"];
        let raw_excludes: [&str; 0] = [];
        let globs = GlobSet {
            include: make_includes(raw_includes),
            exclude: any(raw_excludes).unwrap(),
            exclude_raw: raw_excludes.iter().map(|s| s.to_string()).collect(),
        };

        let hash = "the-hash".to_string();

        glob_watcher
            .watch_globs(hash.clone(), globs, timeout)
            .await
            .unwrap();

        let candidates = HashSet::from_iter(raw_includes.iter().map(|s| s.to_string()));
        let results = glob_watcher
            .get_changed_globs(hash.clone(), candidates.clone(), timeout)
            .await
            .unwrap();
        assert!(results.is_empty());

        let second_raw_includes = &["my-pkg/.next/**"];
        let second_raw_excludes = ["my-pkg/.next/cache/**"];
        let second_globs = GlobSet {
            include: make_includes(second_raw_includes),
            exclude: any(second_raw_excludes).unwrap(),
            exclude_raw: second_raw_excludes.iter().map(|s| s.to_string()).collect(),
        };
        let second_hash = "the-second-hash".to_string();
        glob_watcher
            .watch_globs(second_hash.clone(), second_globs, timeout)
            .await
            .unwrap();

        let second_candidates =
            HashSet::from_iter(second_raw_includes.iter().map(|s| s.to_string()));
        let results = glob_watcher
            .get_changed_globs(hash.clone(), candidates.clone(), timeout)
            .await
            .unwrap();
        assert!(results.is_empty());

        let results = glob_watcher
            .get_changed_globs(second_hash.clone(), second_candidates.clone(), timeout)
            .await
            .unwrap();
        assert!(results.is_empty());

        // Make a change that is excluded in one of the hashes but not in the other
        repo_root
            .join_components(&["my-pkg", ".next", "cache", "foo"])
            .create_with_contents("hello")
            .unwrap();
        // expect one changed glob for the first hash
        let results = glob_watcher
            .get_changed_globs(hash.clone(), candidates.clone(), timeout)
            .await
            .unwrap();
        let expected = HashSet::from_iter(["my-pkg/.next/**".to_string()]);
        assert_eq!(results, expected);

        // The second hash which excludes the change should still not have any changed
        // globs
        let results = glob_watcher
            .get_changed_globs(second_hash.clone(), second_candidates.clone(), timeout)
            .await
            .unwrap();
        assert!(results.is_empty());

        // Make a change for second_hash
        repo_root
            .join_components(&["my-pkg", ".next", "bar"])
            .create_with_contents("hello")
            .unwrap();
        let results = glob_watcher
            .get_changed_globs(second_hash.clone(), second_candidates.clone(), timeout)
            .await
            .unwrap();
        assert_eq!(results, second_candidates);
    }