1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use std::fmt::Display;

pub const AMBIENT_WEB_APP_URL: &str = "https://ambient.run";
pub const ASSETS_URL: &str = "https://assets.ambient.run";
pub const API_URL: &str = "https://api.ambient.run";

/// The URL for a deployed package on the website.
///
/// What the user would visit to play this package on the website.
pub fn web_package_url(package_id: &str, version: Option<&str>) -> String {
    let mut output = format!("{AMBIENT_WEB_APP_URL}/packages/{package_id}");
    if let Some(version) = version {
        output.push_str(&format!("/version/{version}"));
    }
    output
}

/// The URL for a deployed package on the asset server.
///
/// What the user would use to run this package, or to get its assets.
pub fn deployment_url(deployment_id: &str) -> String {
    format!("{ASSETS_URL}/{deployment_id}")
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ServerSelector<'a> {
    Deployment(&'a str),
    Package {
        id: &'a str,
        version: Option<&'a str>,
    },
}
impl Display for ServerSelector<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ServerSelector::Deployment(id) => write!(f, "deployment_id={id}"),
            ServerSelector::Package { id, version } => {
                write!(f, "package_id={id}")?;
                if let Some(version) = version {
                    write!(f, "&version={version}")?;
                }
                Ok(())
            }
        }
    }
}

/// The URL for a ensure-running server for a deployed package.
///
/// When connecting to this URL, a server will be started if one is not already running.
pub fn ensure_running_url(selector: ServerSelector) -> String {
    format!("{API_URL}/servers/ensure-running?{selector}")
}

/// Replicated from `AmbientFbSchema::DbPackageContent`
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PackageContent {
    Playable,
    Example,
    NotExample,
    Asset,
    Models,
    Animations,
    Textures,
    Materials,
    Fonts,
    Code,
    Schema,
    Audio,
    Other,
    Tool,
    Mod,
}

#[derive(Default, Copy, Clone)]
pub struct PackageListParams<'a> {
    /// ISO8601 date string
    pub created_after: Option<&'a str>,
    /// ISO8601 date string
    pub updated_after: Option<&'a str>,
    /// NOTE: This does not URI-encode the name (yet)
    pub name: Option<&'a str>,
    pub content_contains: &'a [PackageContent],
    pub owner_id: Option<&'a str>,
    pub for_playable: Option<&'a str>,
}

/// Endpoint to get a list of packages. Filters can be supplied as necessary.
pub fn package_list_url(params: PackageListParams) -> String {
    let url = format!("{API_URL}/packages/list");

    let mut segments = vec![];
    if let Some(created_after) = params.created_after {
        segments.push(format!("created_after={}", created_after));
    }

    if let Some(updated_after) = params.updated_after {
        segments.push(format!("updated_after={}", updated_after));
    }

    if let Some(name) = params.name {
        segments.push(format!("name={}", name));
    }

    if !params.content_contains.is_empty() {
        segments.push(format!(
            "content_contains={}",
            params
                .content_contains
                .iter()
                .map(|content| format!("{:?}", content))
                .collect::<Vec<_>>()
                .join(",")
        ));
    }

    if let Some(owner_id) = params.owner_id {
        segments.push(format!("owner_id={}", owner_id));
    }

    if let Some(for_playable) = params.for_playable {
        segments.push(format!("for_playable={}", for_playable));
    }

    if segments.is_empty() {
        url
    } else {
        format!("{url}?{}", segments.join("&"))
    }
}