Initial working commit
This commit is contained in:
commit
7d74d542db
|
@ -0,0 +1,3 @@
|
||||||
|
libsig/target/
|
||||||
|
sigweb/target/
|
||||||
|
target/
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,5 @@
|
||||||
|
[workspace]
|
||||||
|
members = [
|
||||||
|
"libsig",
|
||||||
|
"sigweb"
|
||||||
|
]
|
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
Cargo.lock
|
|
@ -0,0 +1,14 @@
|
||||||
|
[package]
|
||||||
|
name = "libsig"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rand = "0.8"
|
||||||
|
rand_distr = "0.4"
|
||||||
|
derive_more = "0.99"
|
||||||
|
structopt = "0.3"
|
||||||
|
|
||||||
|
[dependencies.serde]
|
||||||
|
version = "1"
|
||||||
|
features = ["derive"]
|
|
@ -0,0 +1,26 @@
|
||||||
|
use libsig;
|
||||||
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
#[derive(StructOpt)]
|
||||||
|
#[structopt(name="sigcli", about="generate ideas for songs, tracks, and sounds")]
|
||||||
|
struct CliOptions {
|
||||||
|
/// Don't suggest percussion elements
|
||||||
|
#[structopt(short, long)]
|
||||||
|
ambient: bool,
|
||||||
|
/// The number of suggestions to generate
|
||||||
|
#[structopt(default_value="1")]
|
||||||
|
suggestions: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let opt = CliOptions::from_args();
|
||||||
|
|
||||||
|
for _ in 0..opt.suggestions {
|
||||||
|
let idea = if opt.ambient {
|
||||||
|
libsig::SongIdea::generate_ambient()
|
||||||
|
} else {
|
||||||
|
libsig::SongIdea::generate()
|
||||||
|
};
|
||||||
|
println!("{}", idea)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
pub fn display_list(list: &[impl Display]) -> String {
|
||||||
|
match list.len() {
|
||||||
|
0 => "".into(),
|
||||||
|
1 => format!("{}", list[0]),
|
||||||
|
2 => format!("{} and {}", list[0], list[1]),
|
||||||
|
_ => format!("{}and {}", display_list_commas(&list[0..list.len()-1]), list[list.len()-1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_list_commas(list: &[impl Display]) -> String {
|
||||||
|
let mut string = String::new();
|
||||||
|
for item in list {
|
||||||
|
string.push_str(&format!("{}, ", item));
|
||||||
|
}
|
||||||
|
string
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn single_item() {
|
||||||
|
let list = ["red"];
|
||||||
|
assert_eq!(&display_list(&list), "red");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn two_items() {
|
||||||
|
let list = ["red", "green"];
|
||||||
|
assert_eq!(&display_list(&list), "red and green");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn three_items() {
|
||||||
|
let list = ["red", "green", "blue"];
|
||||||
|
assert_eq!(&display_list(&list), "red, green, and blue");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn four_items() {
|
||||||
|
let list = ["red", "yellow", "green", "blue"];
|
||||||
|
assert_eq!(&display_list(&list), "red, yellow, green, and blue");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn many_items() {
|
||||||
|
let list = ["red", "orange" , "yellow", "green", "blue", "purple"];
|
||||||
|
assert_eq!(&display_list(&list), "red, orange, yellow, green, blue, and purple");
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use derive_more::Display;
|
||||||
|
use rand::distributions::{Distribution, Standard};
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
use crate::displayutil::display_list;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Copy, Clone, Display, Serialize, Deserialize)]
|
||||||
|
pub enum Effect {
|
||||||
|
#[display(fmt = "delay")]
|
||||||
|
Delay,
|
||||||
|
#[display(fmt = "reverb")]
|
||||||
|
Reverb,
|
||||||
|
#[display(fmt = "lofi")]
|
||||||
|
Lofi,
|
||||||
|
#[display(fmt = "drive")]
|
||||||
|
Drive,
|
||||||
|
#[display(fmt = "chorus")]
|
||||||
|
Chorus,
|
||||||
|
#[display(fmt = "tremolo")]
|
||||||
|
Tremolo,
|
||||||
|
#[display(fmt = "distortion")]
|
||||||
|
Distortion,
|
||||||
|
#[display(fmt = "flanger")]
|
||||||
|
Flanger,
|
||||||
|
#[display(fmt = "phaser")]
|
||||||
|
Phaser,
|
||||||
|
#[display(fmt = "compressor")]
|
||||||
|
Compressor,
|
||||||
|
#[display(fmt = "sustain")]
|
||||||
|
Sustain,
|
||||||
|
#[display(fmt = "bitcrusher")]
|
||||||
|
Bitcrusher,
|
||||||
|
#[display(fmt = "wavefolder")]
|
||||||
|
Wavefolder,
|
||||||
|
#[display(fmt = "ring modulator")]
|
||||||
|
RingMod,
|
||||||
|
#[display(fmt = "wah")]
|
||||||
|
Wah,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Distribution<Effect> for Standard {
|
||||||
|
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Effect {
|
||||||
|
let index: u8 = rng.gen_range(0..15);
|
||||||
|
use Effect::*;
|
||||||
|
match index {
|
||||||
|
0 => Delay,
|
||||||
|
1 => Reverb,
|
||||||
|
2 => Lofi,
|
||||||
|
3 => Drive,
|
||||||
|
4 => Chorus,
|
||||||
|
5 => Tremolo,
|
||||||
|
6 => Distortion,
|
||||||
|
7 => Flanger,
|
||||||
|
8 => Phaser,
|
||||||
|
9 => Compressor,
|
||||||
|
10 => Sustain,
|
||||||
|
11 => Bitcrusher,
|
||||||
|
12 => Wavefolder,
|
||||||
|
13 => RingMod,
|
||||||
|
14 => Wah,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_effects() -> Vec<Effect> {
|
||||||
|
let mut vec = Vec::<Effect>::new();
|
||||||
|
while rand::random() {
|
||||||
|
vec.push(rand::random())
|
||||||
|
}
|
||||||
|
vec
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn display_effects(effects: &[Effect]) -> String {
|
||||||
|
if effects.is_empty() {
|
||||||
|
"clean".into()
|
||||||
|
} else {
|
||||||
|
format!("through {}", display_list(effects))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
mod displayutil;
|
||||||
|
mod effects;
|
||||||
|
mod tempo;
|
||||||
|
mod voices;
|
||||||
|
pub use effects::{display_effects, generate_effects, Effect};
|
||||||
|
pub use tempo::Tempo;
|
||||||
|
pub use voices::{generate_voice_adjective, Voice, display_voices};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct SongIdea {
|
||||||
|
pub tempo: Tempo,
|
||||||
|
pub effects: Vec<Effect>,
|
||||||
|
pub lead: Option<Voice>,
|
||||||
|
pub bass: Option<Voice>,
|
||||||
|
pub pad: Option<Voice>,
|
||||||
|
pub vocals: Option<Voice>,
|
||||||
|
pub kick: Option<Voice>,
|
||||||
|
pub snare: Option<Voice>,
|
||||||
|
pub hat: Option<Voice>,
|
||||||
|
pub tom: Option<Voice>,
|
||||||
|
pub cymbal: Option<Voice>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SongIdea {
|
||||||
|
pub fn generate() -> Self {
|
||||||
|
Self {
|
||||||
|
tempo: rand::random(),
|
||||||
|
effects: generate_effects(),
|
||||||
|
lead: generate_voice_adjective("lead"),
|
||||||
|
bass: generate_voice_adjective("bass"),
|
||||||
|
pad: generate_voice_adjective("pad"),
|
||||||
|
vocals: generate_voice_adjective("vocals"),
|
||||||
|
kick: generate_voice_adjective("kick"),
|
||||||
|
snare: generate_voice_adjective("snare"),
|
||||||
|
hat: generate_voice_adjective("hat"),
|
||||||
|
tom: generate_voice_adjective("tom"),
|
||||||
|
cymbal: generate_voice_adjective("cymbal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_ambient() -> Self {
|
||||||
|
Self {
|
||||||
|
tempo: rand::random(),
|
||||||
|
effects: generate_effects(),
|
||||||
|
lead: generate_voice_adjective("lead"),
|
||||||
|
bass: generate_voice_adjective("bass"),
|
||||||
|
pad: generate_voice_adjective("pad"),
|
||||||
|
vocals: generate_voice_adjective("vocals"),
|
||||||
|
kick: None,
|
||||||
|
snare: None,
|
||||||
|
hat: None,
|
||||||
|
tom: None,
|
||||||
|
cymbal: None
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for SongIdea {
|
||||||
|
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||||
|
let voices: Vec<_> = [&self.lead, &self.bass, &self.pad, &self.vocals, &self.kick, &self.snare, &self.hat, &self.tom, &self.cymbal].iter().cloned().cloned().filter_map(|voice| voice).collect();
|
||||||
|
write!(fmt, "{} at {}, {}", display_voices(&voices), self.tempo, display_effects(&self.effects))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use derive_more::Display;
|
||||||
|
use rand::distributions::{Distribution, Standard};
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Copy, Clone, Display, Serialize, Deserialize)]
|
||||||
|
#[display(fmt="{} bpm, with {} swing", bpm, swing)]
|
||||||
|
pub struct Tempo {
|
||||||
|
bpm: u16,
|
||||||
|
swing: Swing,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Distribution<Tempo> for Standard {
|
||||||
|
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Tempo {
|
||||||
|
// These parameters are static and known-good
|
||||||
|
let raw_bpm_distr: f32 = rand_distr::SkewNormal::new(25.0, 75.0, 5.0).unwrap().sample(rng);
|
||||||
|
let bpm: u16 =
|
||||||
|
if raw_bpm_distr < 33. { 33 } else if raw_bpm_distr > 300.0 { 300 } else { raw_bpm_distr as u16 };
|
||||||
|
let swing: Swing = Standard::sample(&self, rng);
|
||||||
|
Tempo {
|
||||||
|
bpm, swing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Copy, Clone, Display, Serialize, Deserialize)]
|
||||||
|
pub enum Swing {
|
||||||
|
#[display(fmt = "no")]
|
||||||
|
None,
|
||||||
|
#[display(fmt = "light")]
|
||||||
|
Light,
|
||||||
|
#[display(fmt = "heavy")]
|
||||||
|
Heavy
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Distribution<Swing> for Standard {
|
||||||
|
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Swing {
|
||||||
|
let index: u8 = rng.gen_range(0..6);
|
||||||
|
match index {
|
||||||
|
0 | 1 | 2=> Swing::None,
|
||||||
|
3 | 4 => Swing::Light,
|
||||||
|
5 => Swing::Heavy,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fmt_tempo_struct() {
|
||||||
|
assert_eq!(&format!("{}", Tempo { bpm: 80, swing: Swing::Light}), "80 bpm, with light swing");
|
||||||
|
}
|
|
@ -0,0 +1,204 @@
|
||||||
|
use rand::prelude::SliceRandom;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::displayutil::display_list;
|
||||||
|
use derive_more::Display;
|
||||||
|
use rand::distributions::{Distribution, Standard};
|
||||||
|
use rand::Rng;
|
||||||
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Display)]
|
||||||
|
pub enum VoiceAdjective {
|
||||||
|
Glassy,
|
||||||
|
Wide,
|
||||||
|
Glitchy,
|
||||||
|
Aggressive,
|
||||||
|
Warm,
|
||||||
|
Thick,
|
||||||
|
Clean,
|
||||||
|
Hard,
|
||||||
|
Wet,
|
||||||
|
Dry,
|
||||||
|
Thin,
|
||||||
|
Tinny,
|
||||||
|
Splashy,
|
||||||
|
Heavy,
|
||||||
|
Light,
|
||||||
|
Dark,
|
||||||
|
Smooth,
|
||||||
|
Plucky,
|
||||||
|
Soft,
|
||||||
|
Droning,
|
||||||
|
Digital,
|
||||||
|
Squishy,
|
||||||
|
Analogue,
|
||||||
|
Round,
|
||||||
|
Sharp,
|
||||||
|
Crunchy,
|
||||||
|
Aliased,
|
||||||
|
Punchy,
|
||||||
|
Intense,
|
||||||
|
Mild,
|
||||||
|
Calm,
|
||||||
|
Sonorous,
|
||||||
|
Resonant,
|
||||||
|
Buzzy,
|
||||||
|
Dampened,
|
||||||
|
Subdued,
|
||||||
|
Rampant,
|
||||||
|
Hot,
|
||||||
|
Sweaty,
|
||||||
|
Flooded,
|
||||||
|
Plonky,
|
||||||
|
Yoinky,
|
||||||
|
Sploinky,
|
||||||
|
Boofy,
|
||||||
|
Middy,
|
||||||
|
Grimy,
|
||||||
|
Ugly,
|
||||||
|
Soothing,
|
||||||
|
Skittery,
|
||||||
|
Harsh,
|
||||||
|
Alien,
|
||||||
|
Bubbly,
|
||||||
|
Airy,
|
||||||
|
Subterranean,
|
||||||
|
Claustrophobic,
|
||||||
|
Spacious,
|
||||||
|
Ringing,
|
||||||
|
Clicky,
|
||||||
|
Acidic,
|
||||||
|
Solid,
|
||||||
|
Ethereal,
|
||||||
|
Massive,
|
||||||
|
Distant,
|
||||||
|
Muffled,
|
||||||
|
Garbled,
|
||||||
|
Degraded,
|
||||||
|
BlownOut,
|
||||||
|
Sizzling,
|
||||||
|
#[display(fmt = "In-your-face")]
|
||||||
|
Inyouface,
|
||||||
|
#[display(fmt = "Way Huge")]
|
||||||
|
Wayhuge,
|
||||||
|
ElectroHarmonic,
|
||||||
|
Surfy,
|
||||||
|
Nostalgic,
|
||||||
|
Ancient,
|
||||||
|
Angular,
|
||||||
|
Fried,
|
||||||
|
Ineffable,
|
||||||
|
Ramshackle,
|
||||||
|
Mercurial,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
pub struct Voice {
|
||||||
|
adjective: VoiceAdjective,
|
||||||
|
voice: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Distribution<VoiceAdjective> for Standard {
|
||||||
|
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> VoiceAdjective {
|
||||||
|
use VoiceAdjective::*;
|
||||||
|
let values = [
|
||||||
|
Glassy,
|
||||||
|
Wide,
|
||||||
|
Glitchy,
|
||||||
|
Aggressive,
|
||||||
|
Warm,
|
||||||
|
Thick,
|
||||||
|
Clean,
|
||||||
|
Hard,
|
||||||
|
Wet,
|
||||||
|
Dry,
|
||||||
|
Thin,
|
||||||
|
Tinny,
|
||||||
|
Splashy,
|
||||||
|
Heavy,
|
||||||
|
Light,
|
||||||
|
Dark,
|
||||||
|
Smooth,
|
||||||
|
Plucky,
|
||||||
|
Soft,
|
||||||
|
Droning,
|
||||||
|
Digital,
|
||||||
|
Squishy,
|
||||||
|
Analogue,
|
||||||
|
Round,
|
||||||
|
Sharp,
|
||||||
|
Crunchy,
|
||||||
|
Aliased,
|
||||||
|
Punchy,
|
||||||
|
Intense,
|
||||||
|
Mild,
|
||||||
|
Calm,
|
||||||
|
Sonorous,
|
||||||
|
Resonant,
|
||||||
|
Buzzy,
|
||||||
|
Dampened,
|
||||||
|
Subdued,
|
||||||
|
Rampant,
|
||||||
|
Hot,
|
||||||
|
Sweaty,
|
||||||
|
Flooded,
|
||||||
|
Plonky,
|
||||||
|
Yoinky,
|
||||||
|
Sploinky,
|
||||||
|
Boofy,
|
||||||
|
Middy,
|
||||||
|
Grimy,
|
||||||
|
Ugly,
|
||||||
|
Soothing,
|
||||||
|
Skittery,
|
||||||
|
Harsh,
|
||||||
|
Alien,
|
||||||
|
Bubbly,
|
||||||
|
Airy,
|
||||||
|
Subterranean,
|
||||||
|
Claustrophobic,
|
||||||
|
Spacious,
|
||||||
|
Ringing,
|
||||||
|
Clicky,
|
||||||
|
Acidic,
|
||||||
|
Solid,
|
||||||
|
Ethereal,
|
||||||
|
Massive,
|
||||||
|
Distant,
|
||||||
|
Muffled,
|
||||||
|
Garbled,
|
||||||
|
Degraded,
|
||||||
|
BlownOut,
|
||||||
|
Sizzling,
|
||||||
|
Inyouface,
|
||||||
|
Wayhuge,
|
||||||
|
ElectroHarmonic,
|
||||||
|
Surfy,
|
||||||
|
Nostalgic,
|
||||||
|
Ancient,
|
||||||
|
Angular,
|
||||||
|
Fried,
|
||||||
|
Ineffable,
|
||||||
|
Ramshackle,
|
||||||
|
Mercurial,
|
||||||
|
];
|
||||||
|
values.choose(rng).unwrap().clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_voice_adjective(voice: &'static str) -> Option<Voice> {
|
||||||
|
if rand::random() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(Voice {
|
||||||
|
adjective: rand::random(),
|
||||||
|
voice: voice.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn display_voices(list: &[Voice]) -> String {
|
||||||
|
let voices_formatted: Vec<_> = list
|
||||||
|
.iter()
|
||||||
|
.map(|voice| format!("{} {}", voice.adjective, voice.voice).to_lowercase())
|
||||||
|
.collect();
|
||||||
|
display_list(&voices_formatted)
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
/target
|
|
@ -0,0 +1,7 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sigweb"
|
||||||
|
version = "0.1.0"
|
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "sigweb"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = "1"
|
||||||
|
serde_json = "1"
|
||||||
|
libsig = { path = "../libsig" }
|
||||||
|
|
||||||
|
[dependencies.rocket]
|
||||||
|
version = "0.5.0-rc.1"
|
||||||
|
features = ["json"]
|
|
@ -0,0 +1,49 @@
|
||||||
|
#[macro_use]
|
||||||
|
extern crate rocket;
|
||||||
|
use libsig;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use rocket::serde::json::Json;
|
||||||
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
|
#[get("/api")]
|
||||||
|
fn api_version_available() -> Json<Value> {
|
||||||
|
Json(json! ({
|
||||||
|
"versions": [ "v1" ]
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/api/v1")]
|
||||||
|
fn api_v1_index() -> Json<Value> {
|
||||||
|
Json(json! ({
|
||||||
|
"endpoints": [ "generate", "generate_ambient" ]
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
struct Output {
|
||||||
|
data: libsig::SongIdea,
|
||||||
|
text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/api/v1/generate")]
|
||||||
|
fn generate() -> Json<Output> {
|
||||||
|
let idea = libsig::SongIdea::generate();
|
||||||
|
Json(Output {
|
||||||
|
data: idea.clone(),
|
||||||
|
text: idea.to_string()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/api/v1/generate_ambient")]
|
||||||
|
fn generate_ambient() -> Json<Output> {
|
||||||
|
let idea = libsig::SongIdea::generate_ambient();
|
||||||
|
Json(Output {
|
||||||
|
data: idea.clone(),
|
||||||
|
text: idea.to_string()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[launch]
|
||||||
|
fn rocket() -> _ {
|
||||||
|
rocket::build().mount("/", routes![api_version_available, api_v1_index, generate, generate_ambient])
|
||||||
|
}
|
Loading…
Reference in New Issue