///|
fn npm_command(source : Source, scope : Scope) -> Command {
{
program: "npm",
args: [
"config",
"set",
"registry",
source.url,
"--location=\{scope.to_text()}",
],
}
}
///|
fn pip_command(source : Source, scope : Scope) -> Command {
let scope_flag = match scope {
Project => "--site"
User => "--user"
System => "--global"
}
{
program: "python",
args: [
"-m",
"pip",
"config",
scope_flag,
"set",
"global.index-url",
source.url,
],
}
}
///|
fn pnpm_command(source : Source, scope : Scope) -> Command {
let location = match scope {
Project => "project"
User | System => "global"
}
{
program: "pnpm",
args: ["config", "set", "--location=\{location}", "registry", source.url],
}
}
///|
fn yarn_command(source : Source, scope : Scope) -> Command {
let args = ["config", "set", "npmRegistryServer", source.url]
if scope == User {
args.push("--home")
}
{ program: "yarn", args, }
}
///|
fn go_command(source : Source) -> Command {
{ program: "go", args: ["env", "-w", "GOPROXY=\{source.url}"], }
}
///|
fn npm_get_command(scope : Scope) -> Command {
{
program: "npm",
args: ["config", "get", "registry", "--location=\{scope.to_text()}"],
}
}
///|
fn pnpm_get_command(scope : Scope) -> Command {
let location = match scope {
Project => "project"
User | System => "global"
}
{
program: "pnpm",
args: ["config", "get", "--location=\{location}", "registry"],
}
}
///|
fn yarn_get_command() -> Command {
{ program: "yarn", args: ["config", "get", "npmRegistryServer"], }
}
///|
fn pip_get_command(scope : Scope) -> Command {
let scope_flag = match scope {
Project => "--site"
User => "--user"
System => "--global"
}
{
program: "python",
args: ["-m", "pip", "config", scope_flag, "get", "global.index-url"],
}
}
///|
fn go_get_command() -> Command {
{ program: "go", args: ["env", "GOPROXY"], }
}
///|
fn composer_config_args(scope : Scope) -> Array[String] {
let args = ["config"]
if scope == User {
args.push("--global")
}
args
}
///|
fn composer_command(source : Source, scope : Scope) -> Command {
let args = composer_config_args(scope)
args.push("repositories.packagist")
args.push("composer")
args.push(source.url)
{ program: "composer", args, }
}
///|
fn composer_get_command(scope : Scope) -> Command {
let args = composer_config_args(scope)
args.push("repositories.packagist.url")
{ program: "composer", args, }
}
///|
fn cargo_path(scope : Scope) -> ConfigPath {
match scope {
Project => CargoProject
User | System => CargoUser
}
}
///|
fn cargo_set_file_steps(source : Source, scope : Scope) -> Array[FileStep] {
let operation = if source.mirror_id == "official" {
ResetCargoSource
} else {
SetCargoSource("sparse+\{source.url}")
}
[{ path: cargo_path(scope), operation, }]
}
///|
fn cargo_get_file_steps(scope : Scope) -> Array[FileStep] {
[{ path: cargo_path(scope), operation: ReadCargoSource, }]
}
///|
fn cargo_restore_file_steps(
values : ArrayView[SavedValue],
scope : Scope,
) -> Array[FileStep] {
[{ path: cargo_path(scope), operation: RestoreContent(values[0]), }]
}
///|
fn moon_set_file_steps(source : Source, _scope : Scope) -> Array[FileStep] {
let operation = if source.mirror_id == "official" {
ResetMooncakesSource
} else {
SetMooncakesSource(
registry=source.url,
index="\{source.url}.git",
symbols="\{source.url}/symbols.zip",
)
}
[{ path: MoonUser, operation, }]
}
///|
fn moon_get_file_steps(_scope : Scope) -> Array[FileStep] {
[{ path: MoonUser, operation: ReadMooncakesSource, }]
}
///|
fn moon_restore_file_steps(
values : ArrayView[SavedValue],
_scope : Scope,
) -> Array[FileStep] {
[{ path: MoonUser, operation: RestoreContent(values[0]), }]
}
///|
fn require_saved_value(
target : String,
value : SavedValue,
) -> String? raise PlanError {
match value {
Present(source) => Some(source)
Missing => None
Redacted => raise SnapshotNotRestorable(target)
}
}
///|
fn npm_restore_command(
value : SavedValue,
scope : Scope,
) -> Command raise PlanError {
match require_saved_value("npm", value) {
Some(url) => npm_command({ mirror_id: "history", url, }, scope)
None =>
{
program: "npm",
args: ["config", "delete", "registry", "--location=\{scope.to_text()}"],
}
}
}
///|
fn pnpm_restore_command(
value : SavedValue,
scope : Scope,
) -> Command raise PlanError {
match require_saved_value("pnpm", value) {
Some(url) => pnpm_command({ mirror_id: "history", url, }, scope)
None => {
let location = match scope {
Project => "project"
User | System => "global"
}
{
program: "pnpm",
args: ["config", "delete", "--location=\{location}", "registry"],
}
}
}
}
///|
fn yarn_restore_command(
value : SavedValue,
scope : Scope,
) -> Command raise PlanError {
match require_saved_value("yarn", value) {
Some(url) => yarn_command({ mirror_id: "history", url, }, scope)
None => {
let args = ["config", "unset", "npmRegistryServer"]
if scope == User {
args.push("--home")
}
{ program: "yarn", args, }
}
}
}
///|
fn pip_restore_command(
value : SavedValue,
scope : Scope,
) -> Command raise PlanError {
match require_saved_value("pip", value) {
Some(url) => pip_command({ mirror_id: "history", url, }, scope)
None => {
let scope_flag = match scope {
Project => "--site"
User => "--user"
System => "--global"
}
{
program: "python",
args: ["-m", "pip", "config", scope_flag, "unset", "global.index-url"],
}
}
}
}
///|
fn go_restore_command(value : SavedValue) -> Command raise PlanError {
match require_saved_value("go", value) {
Some(url) => go_command({ mirror_id: "history", url, })
None => { program: "go", args: ["env", "-u", "GOPROXY"], }
}
}
///|
fn composer_restore_command(
value : SavedValue,
scope : Scope,
) -> Command raise PlanError {
match require_saved_value("composer", value) {
Some(url) => composer_command({ mirror_id: "history", url, }, scope)
None => {
let args = composer_config_args(scope)
args.push("--unset")
args.push("repositories.packagist")
{ program: "composer", args, }
}
}
}
///|
fn rubygems_clear_command() -> Command {
{ program: "gem", args: ["sources", "--clear-all", "--force"], }
}
///|
fn rubygems_add_command(url : String) -> Command {
{ program: "gem", args: ["sources", "--add", url, "--force"], }
}
///|
fn rubygems_set_commands(source : Source) -> Array[Command] {
[rubygems_clear_command(), rubygems_add_command(source.url)]
}
///|
fn rubygems_get_command() -> Command {
{ program: "gem", args: ["sources", "--list"], }
}
///|
fn rubygems_restore_commands(
value : SavedValue,
) -> Array[Command] raise PlanError {
let commands = [rubygems_clear_command()]
match require_saved_value("rubygems", value) {
Some(urls) =>
for url in urls.split("\n") {
let source = url.trim(chars=" \t\r")
if source != "" {
commands.push(rubygems_add_command(source.to_owned()))
}
}
None => ()
}
commands
}
///|
fn flatpak_scope_flag(scope : Scope) -> String {
match scope {
User => "--user"
System => "--system"
Project => "--user"
}
}
///|
fn flatpak_command(source : Source, scope : Scope) -> Command {
{
program: "flatpak",
args: [
flatpak_scope_flag(scope),
"remote-modify",
"--url=\{source.url}",
"flathub",
],
}
}
///|
fn flatpak_get_command(scope : Scope) -> Command {
{
program: "flatpak",
args: [flatpak_scope_flag(scope), "remotes", "--columns=name,url"],
}
}
///|
fn flatpak_restore_command(
value : SavedValue,
scope : Scope,
) -> Command raise PlanError {
match require_saved_value("flathub", value) {
Some(url) => flatpak_command({ mirror_id: "history", url, }, scope)
None =>
{
program: "flatpak",
args: [flatpak_scope_flag(scope), "remote-delete", "--force", "flathub"],
}
}
}
///|
fn require_snapshot_size(
target : String,
values : ArrayView[SavedValue],
expected : Int,
) -> Unit raise PlanError {
if values.length() != expected {
raise InvalidSnapshot(target~, expected~, actual=values.length())
}
}
///|
/// Resolve a target, mirror, and scope into an exact command plan.
pub fn plan_change(
target_name : StringView,
mirror_name : StringView,
scope? : Scope = User,
) -> ChangePlan raise PlanError {
let recipe = match find_recipe(target_name) {
Some(recipe) => recipe
None => raise UnknownTarget(target_name.to_owned())
}
let target = recipe.target
if !target.scopes.contains(scope) {
raise UnsupportedScope(target=target.id, scope~)
}
let mirror_id = mirror_name.to_owned().to_lower()
let source = match target.find_source(mirror_id) {
Some(source) => source
None => raise UnknownMirror(target=target.id, mirror=mirror_id)
}
let commands = (recipe.set_commands)(source, scope)
let file_steps = (recipe.set_file_steps)(source, scope)
{
operation: SetSource,
target_id: target.id,
mirror_id: Some(mirror_id),
scope,
commands,
file_steps,
}
}
///|
/// Reset a target to its official upstream source.
pub fn plan_reset(
target_name : StringView,
scope? : Scope = User,
) -> ChangePlan raise PlanError {
let plan = plan_change(target_name, "official", scope~)
{ ..plan, operation: ResetSource, }
}
///|
/// Select the first maintainer-preferred mirror. Official upstream is kept as
/// the reset destination and is therefore skipped here.
pub fn plan_first(
target_name : StringView,
scope? : Scope = User,
) -> ChangePlan raise PlanError {
let target = match find_target(target_name) {
Some(target) => target
None => raise UnknownTarget(target_name.to_owned())
}
let source = match
target.sources.search_by(source => source.mirror_id != "official") {
Some(index) => target.sources[index]
None => raise NoSelectableMirror(target.id)
}
plan_change(target.id, source.mirror_id, scope~)
}
///|
/// Build a read-only plan that prints the currently effective source.
pub fn plan_get(
target_name : StringView,
scope? : Scope = User,
) -> ChangePlan raise PlanError {
let recipe = match find_recipe(target_name) {
Some(recipe) => recipe
None => raise UnknownTarget(target_name.to_owned())
}
let target = recipe.target
if !target.scopes.contains(scope) {
raise UnsupportedScope(target=target.id, scope~)
}
let commands = (recipe.get_commands)(scope)
let file_steps = (recipe.get_file_steps)(scope)
{
operation: GetSource,
target_id: target.id,
mirror_id: None,
scope,
commands,
file_steps,
}
}
///|
/// Convert a previously captured snapshot into exact restoration commands.
pub fn plan_restore(snapshot : Snapshot) -> ChangePlan raise PlanError {
let recipe = match find_recipe(snapshot.target_id) {
Some(recipe) => recipe
None => raise UnknownTarget(snapshot.target_id)
}
if !snapshot.is_restorable() {
raise SnapshotNotRestorable(snapshot.target_id)
}
let commands = (recipe.restore_commands)(snapshot.values, snapshot.scope)
let file_steps = (recipe.restore_file_steps)(snapshot.values, snapshot.scope)
{
operation: RestoreSource,
target_id: snapshot.target_id,
mirror_id: None,
scope: snapshot.scope,
commands,
file_steps,
}
}