PPysolate SpineREAD THE CODE · UNDERSTAND THE SYSTEM
课程与章节
源码索引 / CURRENT IMPLEMENTATION

cmd/walkthrough/main.go

171 行 · 点击行号可定位;函数链接随声明位置更新,不手填行号。

跳到函数 / 声明
package main

import (
	"context"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io"
	"os"
	"strings"
	"time"

	spine "pysolate-spine"
)

type scenario string

const (
	normal  scenario = "normal"
	denied  scenario = "denied"
	unknown scenario = "unknown"
)

type report struct {
	Title string `json:"title"`
	Items int    `json:"items"`
}

type notice struct {
	Status string `json:"status"`
}

type notificationHost struct {
	scenario         scenario
	events           []string
	calls            map[string]int
	args             map[string][]json.RawMessage
	serviceDelivered bool // Independent evaluation state; never returned to Guest.
}

func newNotificationHost(s scenario) *notificationHost {
	return &notificationHost{scenario: s, calls: make(map[string]int), args: make(map[string][]json.RawMessage)}
}

func (h *notificationHost) record(name string, args json.RawMessage) {
	h.calls[name]++
	h.args[name] = append(h.args[name], append(json.RawMessage(nil), args...))
}

func (h *notificationHost) manifest() spine.Manifest {
	return spine.Manifest{
		"get_report": {Call: func(_ context.Context, args json.RawMessage) (any, error) {
			h.record("get_report", args)
			var empty map[string]any
			if err := json.Unmarshal(args, &empty); err != nil || len(empty) != 0 {
				return nil, errors.New("get_report expects no arguments")
			}
			h.events = append(h.events, "get_report({}) -> report")
			return report{Title: "Daily summary", Items: 3}, nil
		}},
		"send_notification": {Call: func(_ context.Context, args json.RawMessage) (any, error) {
			h.record("send_notification", args)
			var request struct {
				Message string `json:"message"`
			}
			if err := json.Unmarshal(args, &request); err != nil || request.Message != "Daily summary: 3 items" {
				return nil, errors.New("send_notification received an unexpected message")
			}
			status := "sent"
			switch h.scenario {
			case normal:
				h.serviceDelivered = true
			case denied:
				status = "denied" // Rejected before any simulated dispatch.
			case unknown:
				status = "unknown"
				h.serviceDelivered = true // Dispatch happened; confirmation was lost.
			default:
				return nil, fmt.Errorf("unsupported scenario: %s", h.scenario)
			}
			h.events = append(h.events, fmt.Sprintf("send_notification(message=%q) -> %s", request.Message, status))
			return notice{Status: status}, nil
		}},
	}
}

func validScenario(value string) (scenario, error) {
	switch scenario(value) {
	case normal, denied, unknown:
		return scenario(value), nil
	default:
		return "", fmt.Errorf("invalid scenario %q (choose normal, denied, unknown, or all)", value)
	}
}

func main() {
	if err := runCLI(os.Args[1:], os.Stdout, os.Stderr); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func runCLI(args []string, stdout, stderr io.Writer) error {
	flags := flag.NewFlagSet("walkthrough", flag.ContinueOnError)
	flags.SetOutput(stderr)
	artifact := flags.String("wasm", "dist/spine.wasm", "path to an existing CPython Wasm artifact (does not build or download it)")
	selected := flags.String("scenario", "all", "notification case: normal, denied, unknown, or all")
	if err := flags.Parse(args); err != nil {
		if errors.Is(err, flag.ErrHelp) {
			return nil
		}
		return err
	}
	if flags.NArg() != 0 {
		return fmt.Errorf("unexpected positional arguments: %s", strings.Join(flags.Args(), " "))
	}
	code, err := os.ReadFile("examples/notification.py")
	if err != nil {
		return err
	}
	cases := []scenario{normal, denied, unknown}
	if *selected != "all" {
		one, err := validScenario(*selected)
		if err != nil {
			return err
		}
		cases = []scenario{one}
	}
	wasm, err := os.ReadFile(*artifact)
	if err != nil {
		return fmt.Errorf("read existing Wasm artifact %q (build it separately; walkthrough never downloads or builds it): %w", *artifact, err)
	}
	fmt.Fprintln(stdout, "Python source (examples/notification.py):")
	fmt.Fprintln(stdout, strings.TrimSpace(string(code)))

	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	for _, current := range cases {
		if err := runScenario(ctx, wasm, string(code), current, stdout); err != nil {
			return err
		}
	}
	return nil
}

func runScenario(ctx context.Context, wasm []byte, source string, current scenario, stdout io.Writer) error {
	host := newNotificationHost(current)
	runner, err := spine.New(ctx, wasm, host.manifest())
	if err != nil {
		return err
	}
	defer runner.Close(context.Background())
	out, runErr := runner.Run(ctx, source, map[string]any{})
	fmt.Fprintf(stdout, "\nScenario: %s\n", current)
	for _, event := range host.events {
		fmt.Fprintf(stdout, "Host: %s\n", event)
	}
	if out.Stdout != "" {
		fmt.Fprintf(stdout, "Guest output: %s", out.Stdout)
		if !strings.HasSuffix(out.Stdout, "\n") {
			fmt.Fprintln(stdout)
		}
	}
	if runErr != nil {
		return fmt.Errorf("Guest run failed in scenario %s: %w", current, runErr)
	}
	fmt.Fprintf(stdout, "Result: %s\n", out.Value)
	fmt.Fprintf(stdout, "Evaluation only (not Guest-visible): simulated service delivered=%t\n", host.serviceDelivered)
	return nil
}