Skip to main content

Executable

Struct Executable 

Source
pub struct Executable {
    pub capability: Capability,
    pub handler: Arc<dyn Handler>,
    pub cases: CaseProvider,
}
Expand description

A descriptor with its behaviour and its benchmark cases: what the builtin source contributes.

Fields§

§capability: Capability

The descriptor.

§handler: Arc<dyn Handler>

The behaviour, behind the JSON boundary.

§cases: CaseProvider

The representative inputs, from the input type’s BenchmarkCases.

Implementations§

Source§

impl Executable

Source

pub fn cancellable(self) -> Self

Declare that this handler looks at its cancellation flag and stops.

The one thing about a capability that its kind cannot decide, because it is a fact about the handler’s own code rather than about what the call means. Everything a client does with it — whether the Cockpit offers a Cancel button, what executions.cancel reports — is read from here, so a handler that ignores its flag never produces a control that lies.

use majordomus_cli::capability;
use majordomus_cli::capability::{BenchmarkCases, CaseContext, Context, CapabilityError, Exposure, NamedCase, Stability};
#[derive(serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
struct In {}
impl BenchmarkCases for In {
    fn benchmark_cases(_: &CaseContext<'_>) -> Vec<NamedCase<Self>> { vec![NamedCase::new("default", In {})] }
}
#[derive(serde::Serialize, schemars::JsonSchema)]
struct Out { ok: bool }
fn walk(ctx: &Context, _: In) -> Result<Out, CapabilityError> {
    if ctx.progress.cancelled() { return Err(ctx.progress.cancellation()); }
    Ok(Out { ok: true })
}
let e = capability! {
    id: "demo.walk", title: "Walk", description: "Walks.", input: In, output: Out,
    stability: Stability::Experimental, exposure: Exposure::default(), tags: [],
    handler: walk,
}.cancellable();
assert!(e.capability.execution.cancellable);
Source

pub fn writes_repository(self) -> Self

Declare that this handler writes the repository’s own tracked files.

The second thing a kind cannot decide, and for the same reason: two commands are the same kind whether one announces a peer into this process’s memory and the other stamps a field into a record a commit will carry. The difference is the whole of what a caller needs before saying yes, and every projection reads it from here — the Cockpit’s confirmation, and the exposure ceiling that decides which surfaces may reach it at all.

Declaring it is the handler author’s obligation, not an optimisation. A handler that writes a tracked file without saying so is projected as though it did not, onto surfaces whose ceiling exists to exclude exactly that.

use majordomus_cli::capability;
use majordomus_cli::capability::{BenchmarkCases, CapabilityKind, CaseContext, Context, CapabilityError, Effect, Exposure, NamedCase, Stability};
#[derive(serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
struct In {}
impl BenchmarkCases for In {
    fn benchmark_cases(_: &CaseContext<'_>) -> Vec<NamedCase<Self>> { vec![NamedCase::new("default", In {})] }
}
#[derive(serde::Serialize, schemars::JsonSchema)]
struct Out { ok: bool }
fn write(_: &Context, _: In) -> Result<Out, CapabilityError> { Ok(Out { ok: true }) }
let e = capability! {
    id: "demo.write", kind: CapabilityKind::Command, title: "Write", description: "Writes.",
    input: In, output: Out, stability: Stability::Experimental,
    exposure: Exposure::default(), tags: [], handler: write,
}.writes_repository();
assert_eq!(e.capability.execution.effect, Effect::RepositoryMutation);
Source

pub fn skill(self, alias: &str, argument: &str) -> Self

Declare the short name a person invokes this by from an agent’s prompt, and the input field the text typed after it fills. See crate::capability::SkillExposure; the registry refuses a name that is malformed, reserved or taken, and an argument the input does not have.

use majordomus_cli::capability;
use majordomus_cli::capability::{BenchmarkCases, CapabilityKind, CaseContext, Context, CapabilityError, Exposure, NamedCase, Stability};
#[derive(serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
struct In { text: String }
impl BenchmarkCases for In {
    fn benchmark_cases(_: &CaseContext<'_>) -> Vec<NamedCase<Self>> { vec![] }
}
#[derive(serde::Serialize, schemars::JsonSchema)]
struct Out {}
fn say(_: &Context, _: In) -> Result<Out, CapabilityError> { Ok(Out {}) }
let e = capability! {
    id: "demo.say", kind: CapabilityKind::Command, title: "Say", description: "Says.",
    input: In, output: Out, stability: Stability::Experimental,
    exposure: Exposure::default(), tags: [], handler: say,
}.skill("say", "text");
let skill = e.capability.skill.unwrap();
assert_eq!((skill.alias.as_str(), skill.argument.as_str()), ("say", "text"));
Source

pub fn authenticates_its_input(self) -> Self

Declare that the input is a signed message the handler verifies before it changes anything, so a caller on another host is admitted by its key rather than refused by its address. See crate::capability::ExecutionPolicy::authenticates_its_input.

use majordomus_cli::capability;
use majordomus_cli::capability::{BenchmarkCases, CapabilityKind, CaseContext, Context, CapabilityError, Effect, Exposure, NamedCase, Stability};
#[derive(serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
struct Signed {}
impl BenchmarkCases for Signed {
    fn benchmark_cases(_: &CaseContext<'_>) -> Vec<NamedCase<Self>> { vec![NamedCase::new("default", Signed {})] }
}
#[derive(serde::Serialize, schemars::JsonSchema)]
struct Out { ok: bool }
fn hello(_: &Context, _: Signed) -> Result<Out, CapabilityError> { Ok(Out { ok: true }) }
let e = capability! {
    id: "demo.hello", kind: CapabilityKind::Command, title: "Hello", description: "Verifies a signed hello.",
    input: Signed, output: Out, stability: Stability::Experimental,
    exposure: Exposure::default(), tags: [], handler: hello,
}.authenticates_its_input();
assert!(e.capability.execution.signed_input && e.capability.execution.admits_remote());
assert_eq!(e.capability.execution.effect, Effect::ProcessState);

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more