basic/
basic.rs

1#![allow(dead_code)]
2#![feature(impl_trait_in_assoc_type)]
3
4use serde::Deserialize;
5use thiserror::Error;
6use tokio::io;
7use tosic_http::body::BoxBody;
8use tosic_http::error::response_error::ResponseError;
9use tosic_http::extractors::json::Json;
10use tosic_http::extractors::path::Path as HttpPath;
11use tosic_http::extractors::query::Query;
12use tosic_http::resource::post;
13use tosic_http::response::HttpResponse;
14use tosic_http::server::builder::HttpServerBuilder;
15use tosic_http::traits::responder::Responder;
16use tosic_http_macro::get;
17use tracing::dispatcher::SetGlobalDefaultError;
18
19#[derive(Debug, Error)]
20enum HttpServerError {
21    #[error(transparent)]
22    Tracing(#[from] SetGlobalDefaultError),
23    #[error(transparent)]
24    Io(#[from] io::Error),
25}
26
27impl ResponseError for HttpServerError {}
28
29mod logger {
30    use crate::HttpServerError;
31    #[cfg(feature = "log-subscriber")]
32    use tracing::level_filters::LevelFilter;
33    #[cfg(feature = "log-subscriber")]
34    use tracing_subscriber::fmt::format::FmtSpan;
35    #[cfg(feature = "log-subscriber")]
36    use tracing_subscriber::fmt::Layer as FmtLayer;
37    #[cfg(feature = "log-subscriber")]
38    use tracing_subscriber::layer::SubscriberExt;
39    #[cfg(feature = "log-subscriber")]
40    use tracing_subscriber::{EnvFilter, Layer, Registry};
41
42    #[cfg(feature = "log-subscriber")]
43    pub fn init_tracing() -> Result<(), HttpServerError> {
44        let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| {
45            #[cfg(not(debug_assertions))]
46            let level = LevelFilter::INFO;
47
48            #[cfg(debug_assertions)]
49            let level = LevelFilter::DEBUG;
50
51            EnvFilter::builder()
52                .with_default_directive(level.into())
53                .from_env_lossy()
54        });
55        let def_layer = FmtLayer::new()
56            .with_line_number(true)
57            .with_span_events(FmtSpan::CLOSE)
58            .with_level(true)
59            .with_target(true)
60            .with_thread_names(true)
61            .with_thread_ids(true)
62            .compact()
63            .with_filter(filter);
64
65        let subscriber = Registry::default().with(def_layer);
66
67        tracing::subscriber::set_global_default(subscriber)?;
68
69        Ok(())
70    }
71
72    #[cfg(all(feature = "console-subscriber", not(feature = "log-subscriber")))]
73    pub fn init_tracing() -> Result<(), HttpServerError> {
74        console_subscriber::init();
75
76        Ok(())
77    }
78}
79
80#[derive(Deserialize, Debug)]
81struct TestTest {
82    username: String,
83    password: String,
84}
85
86#[get("/{id}/{name}")]
87#[tracing::instrument]
88async fn not_working(
89    path: HttpPath<(u8, String)>,
90) -> Result<impl Responder<Body = BoxBody>, HttpServerError> {
91    let (id, name) = path.into_inner();
92
93    Ok(HttpResponse::new(200).body((id.to_string(), name)))
94}
95
96async fn test_handler(
97    query: Option<Query<TestTest>>,
98    json: Json<TestTest>,
99) -> impl Responder<Body = BoxBody> {
100    let json = json.into_inner();
101
102    if let Some(query) = query {
103        let test = query.into_inner();
104
105        assert_eq!(test.username, json.username);
106        assert_eq!(test.password, json.password);
107    }
108
109    format!("Hello, {}!", json.username)
110}
111
112async fn test_get() -> impl Responder<Body = BoxBody> {
113    "hi"
114}
115
116#[get("/test")]
117async fn test_fn() -> impl Responder<Body = BoxBody> {
118    "hello testing world"
119}
120
121struct State {
122    dir: String,
123}
124
125/*#[get("**")]
126#[tracing::instrument]
127async fn website(req: HttpRequest, data: Data<State>) -> impl Responder<Body = BoxBody> {
128    const DEFAULT_URL: &str = "index.html";
129    let base_path = data.0.dir.clone();
130
131    let file = match req.params().get("wildcard_deep") {
132        Some(path) => {
133            let path = Path::new(path);
134
135            if path.starts_with("static.files") {
136                // append ../
137                path.to_path_buf()
138            } else {
139                path.to_path_buf()
140            }
141        }
142        None => Path::new(DEFAULT_URL).to_path_buf(),
143    };
144
145    let path = Path::new(&base_path).join(file);
146
147    dbg!(&path);
148
149    if path.exists() {
150        let content_type = if let Some(ext) = path.extension() {
151            match ext.to_str().unwrap() {
152                "html" => "text/html",
153                "css" => "text/css",
154                _ => "application/octet-stream",
155            }
156        } else {
157            "text/plain"
158        };
159
160        let mut file = match File::open(path).await {
161            Ok(f) => f,
162            Err(err) => panic!("Error reading file {:?}", err),
163        };
164
165        let mut buffer = Vec::new();
166
167        file.read_to_end(&mut buffer)
168            .await
169            .expect("TODO: panic message");
170        file.flush().await.expect("TODO: panic message flush");
171
172        let body = BoxBody::new(buffer);
173
174        let mut response = HttpResponse::new(200);
175
176        response
177            .headers_mut()
178            .insert(http::header::CONTENT_TYPE, content_type.parse().unwrap());
179
180        response.set_body(body)
181    } else {
182        HttpResponse::new(404)
183    }
184}*/
185
186#[tokio::main(flavor = "multi_thread", worker_threads = 2)]
187//#[tokio::main(flavor = "current_thread")]
188async fn main() -> Result<(), HttpServerError> {
189    logger::init_tracing()?;
190    let state = State {
191        dir: "/Users/emil/projects/tosic-http/target/doc".to_string(),
192    };
193
194    let server = HttpServerBuilder::default()
195        .app_state(state)
196        .bind("0.0.0.0:4221")
197        //.service_method(Method::POST, "/", test_handler)
198        .route(post("/", test_handler).get(test_get))
199        //.service_method(Method::GET, "/bad", not_working)
200        .service(not_working)
201        .service(test_fn)
202        //.service(website)
203        .build()
204        .await?;
205
206    match server.serve().await {
207        Ok(_) => (),
208        Err(e) => panic!("Failed to serve: {}", e),
209    }
210
211    Ok(())
212}