///Selects a resource of type SiloUser (or any of its children, using the functions on this struct) for lookup or fetch
pub enum SiloUser<'a> {
    /// An error occurred while selecting the resource
    ///
    /// This error will be returned by any lookup/fetch attempts.
    Error(Root<'a>, Error),
    /// We're looking for a resource with the given primary key
    ///
    /// This has no parent container -- a by-id lookup is always global
    PrimaryKey(Root<'a>, Uuid),
}
impl<'a> SiloUser<'a> {
    /// Fetch the record corresponding to the selected resource
    ///
    /// This is equivalent to `fetch_for(authz::Action::Read)`.
    pub async fn fetch(
        &self,
    ) -> LookupResult<(authz::SiloUser, nexus_db_model::SiloUser)> {
        self.fetch_for(authz::Action::Read).await
    }
    /// Turn the Result<T, E> of [`fetch`] into a Result<Option<T>, E>.
    pub async fn optional_fetch(
        &self,
    ) -> LookupResult<Option<(authz::SiloUser, nexus_db_model::SiloUser)>> {
        self.optional_fetch_for(authz::Action::Read).await
    }
    /// Fetch the record corresponding to the selected resource and
    /// check whether the caller is allowed to do the specified `action`
    ///
    /// The return value is a tuple that also includes the `authz`
    /// objects for all resources along the path to this one (i.e., all
    /// parent resources) and the authz object for this resource itself.
    /// These objects are useful for identifying those resources by
    /// id, for doing other authz checks, or for looking up related
    /// objects.
    pub async fn fetch_for(
        &self,
        action: authz::Action,
    ) -> LookupResult<(authz::SiloUser, nexus_db_model::SiloUser)> {
        let lookup = self.lookup_root();
        let opctx = &lookup.opctx;
        let datastore = lookup.datastore;
        match self {
            SiloUser::Error(_, error) => Err(error.clone()),
            SiloUser::PrimaryKey(_, v0) => {
                Self::fetch_by_id_for(opctx, datastore, v0, action).await
            }
        }
    }
    /// Turn the Result<T, E> of [`fetch_for`] into a Result<Option<T>, E>.
    pub async fn optional_fetch_for(
        &self,
        action: authz::Action,
    ) -> LookupResult<Option<(authz::SiloUser, nexus_db_model::SiloUser)>> {
        let result = self.fetch_for(action).await;
        match result {
            Err(Error::ObjectNotFound { type_name: _, lookup_type: _ }) => Ok(None),
            _ => Ok(Some(result?)),
        }
    }
    /// Fetch an `authz` object for the selected resource and check
    /// whether the caller is allowed to do the specified `action`
    ///
    /// The return value is a tuple that also includes the `authz`
    /// objects for all resources along the path to this one (i.e., all
    /// parent resources) and the authz object for this resource itself.
    /// These objects are useful for identifying those resources by
    /// id, for doing other authz checks, or for looking up related
    /// objects.
    pub async fn lookup_for(
        &self,
        action: authz::Action,
    ) -> LookupResult<(authz::SiloUser,)> {
        let lookup = self.lookup_root();
        let opctx = &lookup.opctx;
        let (authz_silo_user,) = self.lookup().await?;
        opctx.authorize(action, &authz_silo_user).await?;
        Ok((authz_silo_user,))
    }
    /// Turn the Result<T, E> of [`lookup_for`] into a Result<Option<T>, E>.
    pub async fn optional_lookup_for(
        &self,
        action: authz::Action,
    ) -> LookupResult<Option<(authz::SiloUser,)>> {
        let result = self.lookup_for(action).await;
        match result {
            Err(Error::ObjectNotFound { type_name: _, lookup_type: _ }) => Ok(None),
            _ => Ok(Some(result?)),
        }
    }
    /// Fetch the "authz" objects for the selected resource and all its
    /// parents
    ///
    /// This function does not check whether the caller has permission
    /// to read this information.  That's why it's not `pub`.  Outside
    /// this module, you want `lookup_for(authz::Action)`.
    async fn lookup(&self) -> LookupResult<(authz::SiloUser,)> {
        let lookup = self.lookup_root();
        let opctx = &lookup.opctx;
        let datastore = lookup.datastore;
        match self {
            SiloUser::Error(_, error) => Err(error.clone()),
            SiloUser::PrimaryKey(_, v0) => {
                let (authz_silo_user, _) = Self::lookup_by_id_no_authz(
                        opctx,
                        datastore,
                        v0,
                    )
                    .await?;
                Ok((authz_silo_user,))
            }
        }
    }
    /// Build the `authz` object for this resource
    fn make_authz(
        authz_parent: &authz::Fleet,
        db_row: &nexus_db_model::SiloUser,
        lookup_type: LookupType,
    ) -> authz::SiloUser {
        authz::SiloUser::with_primary_key(authz_parent.clone(), db_row.id(), lookup_type)
    }
    /// Getting the [`LookupPath`] for this lookup
    ///
    /// This is used when we actually query the database.  At that
    /// point, we need the `OpContext` and `DataStore` that are being
    /// used for this lookup.
    fn lookup_root(&self) -> &LookupPath<'a> {
        match &self {
            SiloUser::Error(root, ..) => root.lookup_root(),
            SiloUser::PrimaryKey(root, ..) => root.lookup_root(),
        }
    }
    /// Fetch the database row for a resource by doing a lookup by id
    ///
    /// This function checks whether the caller has permissions to read
    /// the requested data.  However, it's not intended to be used
    /// outside this module.  See `fetch_for(authz::Action)`.
    async fn fetch_by_id_for(
        opctx: &OpContext,
        datastore: &dyn LookupDataStore,
        v0: &Uuid,
        action: authz::Action,
    ) -> LookupResult<(authz::SiloUser, nexus_db_model::SiloUser)> {
        let (authz_silo_user, db_row) = Self::lookup_by_id_no_authz(opctx, datastore, v0)
            .await?;
        opctx.authorize(action, &authz_silo_user).await?;
        Ok((authz_silo_user, db_row))
    }
    /// Lowest-level function for looking up a resource in the database
    /// by id
    ///
    /// This function does not check whether the caller has permission
    /// to read this information.  That's why it's not `pub`.  Outside
    /// this module, you want `fetch()` or `lookup_for(authz::Action)`.
    async fn lookup_by_id_no_authz(
        opctx: &OpContext,
        datastore: &dyn LookupDataStore,
        v0: &Uuid,
    ) -> LookupResult<(authz::SiloUser, nexus_db_model::SiloUser)> {
        use ::nexus_db_schema::schema::silo_user::dsl;
        let db_row = dsl::silo_user
            .filter(dsl::time_deleted.is_null())
            .filter(dsl::id.eq(v0.clone()))
            .select(nexus_db_model::SiloUser::as_select())
            .get_result_async(&*datastore.pool_connection_authorized(opctx).await?)
            .await
            .map_err(|e| {
                public_error_from_diesel(
                    e,
                    ErrorHandler::NotFoundByLookup(
                        ResourceType::SiloUser,
                        LookupType::ById(
                            ::omicron_uuid_kinds::GenericUuid::into_untyped_uuid(*v0),
                        ),
                    ),
                )
            })?;
        let authz_silo_user = Self::make_authz(
            &&authz::FLEET,
            &db_row,
            LookupType::ById(::omicron_uuid_kinds::GenericUuid::into_untyped_uuid(*v0)),
        );
        Ok((authz_silo_user, db_row))
    }
}
