// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_ASH_BOREALIS_INFRA_TRANSITION_H_
#define CHROME_BROWSER_ASH_BOREALIS_INFRA_TRANSITION_H_
#include <memory>
#include "base/functional/bind.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/types/expected.h"
// TODO(b/172501195): Make these available outside namespace borealis.
namespace borealis {
// A transition object is used to represent a transformation of a start state
// |S| into a terminating state |T|. The transition can fail, in which case the
// result is an error state |E|.
//
// The transition centers around a state object of type |S|, which will become
// an instance of |T| if the transition succeeds, and an error |E| if it does
// not.
//
// The transition itself is in one of three modes:
// - A "pending" transition has been created and is waiting for control of the
// |S| instance to begin the transition.
// - A "working" transition has control of the |S| object, and is transforming
// it into |T|. There is no guarantee at this point about the existence of an
// |S| or |T| instance.
// - A "final" transition has completed, and either succeeded (in which case
// a |T| instance exists) or failed (in which case |T| does not exist and an
// |E| error has been generated.
//
// Once the transition is "final" no further work is possible and the transition
// can be deleted.
template <typename S, typename T, typename E>
class Transition {
public:
using InitialState = S;
using FinalState = T;
using ErrorState = E;
using Result = base::expected<std::unique_ptr<T>, E>;
using OnCompleteSignature = void(Result);
using OnCompleteCallback = base::OnceCallback<OnCompleteSignature>;
Transition() = default;
virtual ~Transition() = default;
// Begin the transition, marking this transition as "working" and giving
// ownership of the |S| object to the transition.
void Begin(std::unique_ptr<S> start_instance, OnCompleteCallback callback) {
callback_ = std::move(callback);
Start(std::move(start_instance));
}
protected:
// Override this method to define the work that this transition performs. As
// soon as this function is entered, the transition is "working", until you
// call Succeed() or Fail(), which should be the last things you call.
virtual void Start(std::unique_ptr<S> start_instance) = 0;
// Called when the transition has completed successfully. This should be the
// last thing you do.
void Succeed(std::unique_ptr<T> terminating_instance) {
if (!callback_) {
return;
}
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback_),
Result(std::move(terminating_instance))));
}
// Called when the transition has completed unsuccessfully. This should only
// be called at the very end of the failing transition (including cleanup if
// needed).
void Fail(E error) {
if (!callback_) {
return;
}
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback_),
base::unexpected(std::move(error))));
}
private:
OnCompleteCallback callback_;
};
} // namespace borealis
#endif // CHROME_BROWSER_ASH_BOREALIS_INFRA_TRANSITION_H_