nextcloud-route-extractor 0.1.3

Extract routes from nextcloud apps
Documentation
use crate::attribute_routes::extract_attribute_routes;
use crate::routes_php::extract_routes_php;
use glob::{glob, GlobError, Paths, Pattern};
use std::fs::read_to_string;
use std::path::{Path, PathBuf};
use thiserror::Error;

mod attribute_routes;
mod route;
mod routes_php;

pub use route::*;

pub fn core_routes(path: &str) -> Result<AppRoutes, Error> {
    let mut result = AppRoutes {
        routes: vec![
            Route {
                url: "/core/ajax/update.php".into(),
                name: "core#ajax_update".into(),
                ..Route::default()
            },
            Route {
                url: "/heartbeat".into(),
                name: "core#heartbeat".into(),
                ..Route::default()
            },
        ],
        ocs: vec![],
    };

    let controller_paths = glob(&format!(
        "{}/core/Controller/*Controller.php",
        Pattern::escape(path)
    ))
    .unwrap();
    for routes in extract_controllers(controller_paths) {
        let routes = routes?;
        result.routes.extend_from_slice(&routes.routes);
        result.ocs.extend_from_slice(&routes.ocs);
    }
    Ok(result)
}

pub fn app_routes(path: &str) -> Result<AppRoutes, Error> {
    let routes_php_path = <str as AsRef<Path>>::as_ref(path).join("appinfo/routes.php");
    let mut result = if routes_php_path.exists() {
        let routes_php = read_to_string(&routes_php_path).map_err(|error| Error::Read {
            path: routes_php_path.clone(),
            error,
        })?;
        extract_routes_php(&routes_php_path, &routes_php)
    } else {
        AppRoutes::default()
    };

    let controller_paths = glob(&format!(
        "{}/lib/Controller/*Controller.php",
        Pattern::escape(path)
    ))
    .unwrap();
    for routes in extract_controllers(controller_paths) {
        let routes = routes?;
        result.routes.extend_from_slice(&routes.routes);
        result.ocs.extend_from_slice(&routes.ocs);
    }
    Ok(result)
}

fn extract_controllers(paths: Paths) -> impl Iterator<Item = Result<AppRoutes, Error>> {
    paths.map(|controller_path| {
        let controller_path = controller_path.map_err(Error::Glob)?;
        let controller_code = read_to_string(&controller_path).map_err(|error| Error::Read {
            path: controller_path,
            error,
        })?;
        Ok(extract_attribute_routes(&controller_code))
    })
}

#[derive(Debug, Error)]
pub enum Error {
    #[error("Failed to read {path}: {error:#}")]
    Read {
        path: PathBuf,
        error: std::io::Error,
    },
    #[error("Failed to find controller paths: {0:#}")]
    Glob(GlobError),
}